มีบางสิ่งที่ทำให้ฉันพึงพอใจมากกว่าบรรทัด Bash อันหรูหราหนึ่งบรรทัดที่ทำงานอัตโนมัติหลายชั่วโมงที่น่าเบื่อ
ส่วนหนึ่งของการสำรวจล่าสุดในการสร้างแล็ปท็อปของฉันใหม่โดยอัตโนมัติด้วยสคริปต์ Bash (โพสต์ที่จะมาถึง!) ฉันต้องการหาวิธีที่จะโคลนที่เก็บที่โฮสต์โดย GitHub ของฉันไปยังเครื่องใหม่ได้อย่างง่ายดาย หลังจากสำรวจไปรอบ ๆ ฉันก็เขียนหนึ่งซับที่ทำอย่างนั้น
จากนั้น ด้วยความมุ่งมั่นที่จะไม่ใส่ไข่ทั้งหมดลงในตะกร้าใบเดียวกัน ฉันจึงเขียนบรรทัดเดียวอีกอันหนึ่งเพื่อสร้างและพุชไปยังข้อมูลสำรองที่โฮสต์โดย GitLab โดยอัตโนมัติเช่นกัน มาแล้วจ้า
A Bash one-liner เพื่อโคลนที่เก็บ GitHub ทั้งหมดของคุณ
ข้อแม้:คุณต้องมีรายการที่เก็บ GitHub ที่คุณต้องการโคลน สิ่งที่ดีเกี่ยวกับเรื่องนี้คือ คุณสามารถเลือกพื้นที่เก็บข้อมูลที่คุณต้องการในเครื่องได้อย่างเต็มที่ แทนที่จะเลือกทั้งตัว
คุณสามารถโคลนที่เก็บ GitHub ได้อย่างง่ายดายโดยไม่ต้องป้อนรหัสผ่านในแต่ละครั้งโดยใช้ HTTPS พร้อมข้อมูลรับรองแคช 15 นาทีของคุณ หรือวิธีที่ฉันต้องการโดยเชื่อมต่อกับ GitHub ด้วย SSH เพื่อความกระชับ ฉันจะถือว่าเรากำลังดำเนินการอย่างหลัง และคีย์ SSH ของเราได้รับการตั้งค่าแล้ว
รับรายการ GitHub URL ในไฟล์ gh-repos.txt
เช่นนี้:
[email protected]:username/first-repository.git
[email protected]:username/second-repository.git
[email protected]:username/third-repository.git
เราดำเนินการ:
xargs -n1 git clone < gh-repos.txt
สิ่งนี้จะโคลนที่เก็บทั้งหมดในรายการลงในโฟลเดอร์ปัจจุบัน ซับเดียวแบบเดียวกันนี้ใช้ได้กับ GitLab เช่นกัน หากคุณแทนที่ URL ที่เหมาะสม
เกิดอะไรขึ้นที่นี่
มีสองส่วนในหนึ่งซับนี้:อินพุต ทางด้านขวาตรงข้ามกับสัญชาตญาณ และส่วนที่ทำให้สิ่งต่าง ๆ เกิดขึ้น ทางด้านซ้าย เราสามารถจัดลำดับของส่วนต่างๆ เหล่านี้ให้เข้าใจง่ายขึ้น (อาจจะ?) โดยการเขียนคำสั่งเดียวกันดังนี้:
<gh-repos.txt xargs -n1 git clone
ในการรันคำสั่งสำหรับแต่ละบรรทัดของอินพุตของเรา gh-repos.txt
เราใช้ xargs -n1
. เครื่องมือ xargs
อ่านรายการจากอินพุตและดำเนินการคำสั่งใดๆ ที่พบ (จะ echo
ถ้าหาไม่เจอ) โดยค่าเริ่มต้น จะถือว่ารายการถูกคั่นด้วยช่องว่าง บรรทัดใหม่ยังใช้งานได้และทำให้รายการของเราอ่านง่ายขึ้น แฟล็ก -n1
บอก xargs
เพื่อใช้ 1
อาร์กิวเมนต์ หรือในกรณีของเรา หนึ่งบรรทัดต่อคำสั่ง เราสร้างคำสั่งด้วย git clone
ซึ่ง xargs
จากนั้นดำเนินการสำหรับแต่ละบรรทัด ทาดา.
Bash one-liner เพื่อสร้างและพุชที่เก็บจำนวนมากบน GitLab
GitLab ซึ่งแตกต่างจาก GitHub ให้เราทำสิ่งที่ดีที่เราไม่ต้องใช้เว็บไซต์เพื่อสร้างที่เก็บใหม่ก่อน เราสามารถสร้างที่เก็บ GitLab ใหม่จากเทอร์มินัลของเรา ที่เก็บที่สร้างขึ้นใหม่มีค่าเริ่มต้นเป็นส่วนตัว ดังนั้นหากเราต้องการทำให้เป็นสาธารณะบน GitLab เราจะต้องทำด้วยตนเองในภายหลัง
เอกสาร GitLab บอกให้เรากดสร้างโครงการใหม่โดยใช้ git push --set-upstream
แต่ฉันไม่คิดว่าสิ่งนี้จะสะดวกมากสำหรับการใช้ GitLab เป็นตัวสำรอง ขณะที่ฉันทำงานกับที่เก็บของฉันในอนาคต ฉันต้องการเรียกใช้คำสั่งหนึ่งคำสั่งที่ส่งไปยังทั้ง GitHub และ GitLab โดยไม่ต้องใช้ความพยายามเพิ่มเติมในส่วนของฉัน
ในการทำให้ Bash one-liner ใช้งานได้ เราจำเป็นต้องมีรายการ URL ที่เก็บสำหรับ GitLab (ที่ยังไม่มี) เราสามารถทำได้โดยง่ายโดยการคัดลอกรายการที่เก็บ GitHub เปิดด้วย Vim และทำการค้นหาและแทนที่:
cp gh-repos.txt gl-repos.txt
vim gl-repos.txt
:%s/\<github\>/gitlab/g
:wq
สิ่งนี้สร้าง gl-repos.txt
ซึ่งมีลักษณะดังนี้:
[email protected]:username/first-repository.git
[email protected]:username/second-repository.git
[email protected]:username/third-repository.git
เราสามารถสร้างที่เก็บเหล่านี้บน GitLab เพิ่ม URL เป็นรีโมต และส่งโค้ดของเราไปที่ที่เก็บใหม่โดยเรียกใช้:
awk -F'\/|(\.git)' '{system("cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push")}' gl-repos.txt
อดทนไว้ แล้วฉันจะอธิบายให้ฟัง ตอนนี้โปรดทราบว่า ~/FULL/PATH/
ควรเป็นเส้นทางแบบเต็มไปยังไดเร็กทอรีที่มีที่เก็บ GitHub ของเรา
เราต้องจดสมมติฐานสองสามข้อ:
- ชื่อของไดเร็กทอรีบนเครื่องโลคัลของคุณที่มีที่เก็บจะเหมือนกับชื่อของที่เก็บใน URL (จะเป็นกรณีนี้หากมันถูกโคลนด้วยซับในหนึ่งด้านบน)
- ปัจจุบันแต่ละที่เก็บถูกตรวจสอบไปยังสาขาที่คุณต้องการพุช กล่าวคือ
master
.
สามารถขยายบรรทัดเดียวเพื่อจัดการกับสมมติฐานเหล่านี้ได้ แต่มันเป็นความเห็นที่ต่ำต้อยของผู้เขียนว่า ณ จุดนั้น เราควรเขียนสคริปต์ทุบตีจริงๆ
เกิดอะไรขึ้นที่นี่
Bash one-liner ของเราใช้แต่ละบรรทัด (หรือ URL) ใน gl-repos.txt
ไฟล์เป็นอินพุต ด้วย awk
มันแยกชื่อของไดเร็กทอรีที่มีที่เก็บในเครื่องของเรา และใช้ข้อมูลเหล่านี้เพื่อสร้างคำสั่งที่ใหญ่ขึ้นของเรา หากเราต้อง print
ผลลัพธ์ของ awk
, เราจะเห็น:
cd ~/FULL/PATH/first-repository && git remote set-url origin --add [email protected]:username/first-repository.git && git push
cd ~/FULL/PATH/second-repository && git remote set-url origin --add [email protected]:username/second-repository.git && git push
cd ~/FULL/PATH/third-repository && git remote set-url origin --add [email protected]:username/third-repository.git && git push
มาดูกันว่าเราสร้างคำสั่งนี้อย่างไร
การแยกสตริงด้วย awk
เครื่องมือ awk
สามารถแยกอินพุตตามตัวคั่นฟิลด์ ตัวคั่นเริ่มต้นคืออักขระช่องว่าง แต่เราสามารถเปลี่ยนได้โดยส่ง -F
ธง. นอกจากอักขระเดี่ยวแล้ว เรายังใช้ตัวคั่นฟิลด์นิพจน์ทั่วไปได้อีกด้วย เนื่องจาก URL ที่เก็บของเรามีรูปแบบที่กำหนดไว้ เราจึงสามารถคว้าชื่อที่เก็บโดยขอสตริงย่อยระหว่างอักขระทับ /
และต่อท้าย URL .git
.
วิธีหนึ่งในการบรรลุสิ่งนี้คือการใช้ regex \/|(\.git)
:
\/
เป็น/
. ที่ใช้ Escape ตัวละคร;|
หมายถึง "หรือ" บอก awk ให้ตรงกับนิพจน์ใดนิพจน์(\.git)
คือกลุ่มดักจับที่ส่วนท้ายของ URL ของเราที่ตรงกับ “.git” โดยมี.
อักขระ. นี่เป็นการโกงเล็กน้อย เนื่องจาก “.git” ไม่ได้แบ่งแยกสิ่งใดๆ เลย (อีกด้านหนึ่งไม่มีอะไรเลย) แต่เป็นวิธีง่ายๆ ที่เราจะถอดส่วนนี้ออก
เมื่อเราบอก awk
. แล้ว จะแยกที่ไหน เราสามารถคว้าสตริงย่อยที่ถูกต้องด้วยตัวดำเนินการภาคสนาม เราอ้างอิงฟิลด์ของเราด้วย $
ตามด้วยหมายเลขคอลัมน์ของฟิลด์ ในตัวอย่างของเรา เราต้องการฟิลด์ที่สอง $2
. สตริงย่อยทั้งหมดมีลักษณะดังนี้:
1: [email protected]:username
2: first-repository
ในการใช้ทั้งสตริงหรือในกรณีของเราคือ URL ทั้งหมด เราใช้ตัวดำเนินการฟิลด์ $0
. ในการเขียนคำสั่ง เราเพียงแค่แทนที่ตัวดำเนินการฟิลด์สำหรับชื่อที่เก็บและ URL เรียกใช้สิ่งนี้ด้วย print
ในขณะที่เรากำลังสร้างอยู่นั้น สามารถช่วยให้แน่ใจว่าเรามีพื้นที่ทั้งหมดถูกต้อง
awk -F'\/|(\.git)' '{print "cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push"}' gl-repos.txt
การรันคำสั่ง
เราสร้างคำสั่งภายในวงเล็บของ system()
. โดยใช้สิ่งนี้เป็นผลลัพธ์ของ awk
แต่ละคำสั่งจะทำงานทันทีที่สร้างและส่งออก system()
ฟังก์ชั่นสร้างโปรเซสลูกที่รันคำสั่งของเรา จากนั้นส่งคืนเมื่อคำสั่งเสร็จสิ้น ในภาษาอังกฤษแบบธรรมดา วิธีนี้ช่วยให้เราดำเนินการคำสั่ง Git ในแต่ละที่เก็บได้ทีละรายการ โดยไม่ทำลายกระบวนการหลักของเราที่ awk
กำลังทำสิ่งต่าง ๆ กับไฟล์อินพุตของเรา นี่คือคำสั่งสุดท้ายของเราอีกครั้ง ทั้งหมดรวมกัน
awk -F'\/|(\.git)' '{system("cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push")}' gl-repos.txt
การใช้ข้อมูลสำรองของเรา
ด้วยการเพิ่ม GitLab URLs เป็นรีโมต เราได้ลดความซับซ้อนของกระบวนการพุชไปยังที่เก็บที่โฮสต์ภายนอกทั้งสอง ถ้าเราเรียกใช้ git remote -v
ในไดเร็กทอรีที่เก็บของเรา เราจะเห็น:
origin [email protected]:username/first-repository.git (fetch)
origin [email protected]:username/first-repository.git (push)
origin [email protected]:username/first-repository.git (push)
ตอนนี้เพียงแค่เรียกใช้ git push
โดยไม่มีอาร์กิวเมนต์จะผลักสาขาปัจจุบันไปยังที่เก็บระยะไกลทั้งสอง
เราควรทราบด้วยว่า git pull
โดยทั่วไปจะพยายามดึงจากที่เก็บระยะไกลที่คุณโคลนจากเดิมเท่านั้น (URL ที่ทำเครื่องหมาย (fetch)
ในตัวอย่างของเราด้านบน) การดึงจากที่เก็บ Git หลายอันพร้อมกันนั้นเป็นไปได้ แต่ซับซ้อน และอยู่นอกเหนือขอบเขตของโพสต์นี้ ต่อไปนี้คือคำอธิบายของการกดและดึงไปยังรีโมตหลายตัวเพื่อช่วยให้คุณเริ่มต้นได้ หากคุณสงสัย เอกสาร Git บนรีโมทก็อาจมีประโยชน์เช่นกัน
เพื่ออธิบายรายละเอียดเกี่ยวกับความกระชับของ Bash one-liners
เมื่อเข้าใจ Bash one-liner อาจเป็นทางลัดที่สนุกและสะดวก อย่างน้อยที่สุด พึงระวังเครื่องมืออย่าง xargs
และ awk
สามารถช่วยทำให้การทำงานของเราเป็นไปโดยอัตโนมัติและบรรเทาความน่าเบื่อหน่ายได้มากมาย อย่างไรก็ตาม มีข้อเสียอยู่บ้าง
ในแง่ของเครื่องมือที่เข้าใจง่าย บำรุงรักษา และเข้าถึงได้นั้น Bash one-liners นั้นแย่มาก โดยทั่วไปแล้วการเขียนจะซับซ้อนกว่าสคริปต์ทุบตีโดยใช้ if
หรือ while
วนซ้ำและซับซ้อนกว่าในการอ่านอย่างแน่นอน เป็นไปได้ว่าเมื่อเราเขียน เราจะพลาดคำพูดเดียวหรือวงเล็บปิดที่ใดที่หนึ่ง และฉันหวังว่าบทความนี้จะแสดงให้เห็น พวกเขาสามารถอธิบายได้ไม่น้อยเช่นกัน ทำไมต้องใช้?
ลองนึกภาพอ่านสูตรทำเค้กทีละขั้นตอน คุณเข้าใจวิธีการและส่วนผสม และรวบรวมเสบียงของคุณ จากนั้น เมื่อคุณคิดเกี่ยวกับมัน คุณจะเริ่มตระหนักว่า หากคุณเพียงแค่โยนส่วนผสมทั้งหมดไปที่เตาอบในลำดับที่ถูกต้องอย่างแม่นยำ เค้กก็จะเป็นรูปเป็นร่างขึ้นทันที ลองแล้วได้ผล!
นั่นคงจะน่าพอใจไม่น้อยใช่ไหม