Computer >> Máy Tính >  >> Lập trình >> Lập trình BASH

Cách viết Bash one-liners để nhân bản và quản lý kho GitHub và GitLab

Ít có điều gì khiến tôi hài lòng hơn một dòng Bash thanh lịch có thể tự động hóa hàng giờ làm việc tẻ nhạt.

Là một phần của một số khám phá gần đây về việc tự động tạo lại máy tính xách tay của tôi bằng các tập lệnh Bash (bài đăng sẽ xuất hiện!), Tôi muốn tìm cách dễ dàng sao chép các kho lưu trữ được lưu trữ trên GitHub của mình sang một máy mới. Sau một hồi tìm hiểu kỹ, tôi đã viết ra một lớp lót làm được điều đó.

Sau đó, với tinh thần không đặt tất cả trứng của chúng ta vào cùng một giỏ, tôi đã viết một lớp lót khác để tự động tạo và đẩy sang các bản sao lưu được lưu trữ trên GitLab. Chúng đây.

Một lớp lót Bash để sao chép tất cả các kho lưu trữ GitHub của bạn

Lưu ý:bạn sẽ cần một danh sách các kho lưu trữ GitHub mà bạn muốn sao chép. Điều tốt về điều đó là nó cung cấp cho bạn toàn quyền để chỉ chọn các kho lưu trữ bạn muốn trên máy của mình, thay vì đi toàn bộ.

Bạn có thể dễ dàng sao chép kho lưu trữ GitHub mà không cần nhập mật khẩu mỗi lần bằng cách sử dụng HTTPS với thông tin đăng nhập được lưu trong bộ nhớ cache trong 15 phút của bạn hoặc, phương pháp ưa thích của tôi, bằng cách kết nối với GitHub bằng SSH. Để ngắn gọn, tôi sẽ cho rằng chúng ta sẽ làm với cái sau và khóa SSH của chúng ta đã được thiết lập.

Cung cấp danh sách các URL GitHub trong tệp gh-repos.txt , như thế này:

[email protected]:username/first-repository.git
[email protected]:username/second-repository.git
[email protected]:username/third-repository.git

Chúng tôi điều hành:

xargs -n1 git clone < gh-repos.txt

Điều này sao chép tất cả các kho lưu trữ trong danh sách vào thư mục hiện tại. Cùng một lớp lót này cũng hoạt động cho GitLab, nếu bạn thay thế các URL thích hợp.

Chuyện gì đang xảy ra ở đây?

Có hai nửa đối với lớp lót một bên này:phần đầu vào, ngược lại ở phía bên phải và phần tạo ra mọi thứ, ở bên trái. Chúng ta có thể làm cho thứ tự của các phần này trực quan hơn (có thể không?) Bằng cách viết cùng một lệnh như sau:

<gh-repos.txt xargs -n1 git clone 

Để chạy lệnh cho mỗi dòng đầu vào của chúng tôi, hãy gh-repos.txt , chúng tôi sử dụng xargs -n1 . Công cụ xargs đọc các mục từ đầu vào và thực hiện bất kỳ lệnh nào mà nó tìm thấy (nó sẽ echo nếu nó không tìm thấy bất kỳ). Theo mặc định, nó giả định rằng các mục được phân tách bằng dấu cách; các dòng mới cũng hoạt động và làm cho danh sách của chúng tôi dễ đọc hơn. Cờ -n1 nói với xargs để sử dụng 1 đối số, hoặc trong trường hợp của chúng tôi, một dòng, mỗi lệnh. Chúng tôi xây dựng lệnh của mình với git clone , mà xargs sau đó thực thi cho mỗi dòng. Ta-da.

A Bash one-liner để tạo và đẩy nhiều kho lưu trữ trên GitLab

GitLab, không giống như GitHub, cho phép chúng tôi thực hiện điều tiện lợi này mà trước tiên chúng tôi không phải sử dụng trang web để tạo một kho lưu trữ mới. Chúng tôi có thể tạo một kho lưu trữ GitLab mới từ thiết bị đầu cuối của mình. Kho lưu trữ mới được tạo mặc định được đặt là Riêng tư, vì vậy nếu chúng tôi muốn đặt nó ở chế độ Công khai trên GitLab, chúng tôi sẽ phải thực hiện việc đó theo cách thủ công sau này.

Tài liệu GitLab yêu cầu chúng tôi thúc đẩy để tạo một dự án mới bằng cách sử dụng git push --set-upstream , nhưng tôi không thấy điều này rất thuận tiện khi sử dụng GitLab làm bản sao lưu. Khi tôi làm việc với kho lưu trữ của mình trong tương lai, tôi muốn chạy một lệnh đẩy đến cả GitHub GitLab mà không cần thêm nỗ lực từ phía tôi.

Để làm cho một lớp lót Bash này hoạt động, chúng tôi cũng sẽ cần danh sách các URL kho lưu trữ cho GitLab (những URL chưa tồn tại). Chúng tôi có thể dễ dàng thực hiện việc này bằng cách sao chép danh sách kho lưu trữ GitHub của mình, mở nó bằng Vim và thực hiện tìm kiếm và thay thế:

cp gh-repos.txt gl-repos.txt
vim gl-repos.txt
:%s/\<github\>/gitlab/g
:wq

Điều này tạo ra gl-repos.txt , trông giống như:

[email protected]:username/first-repository.git
[email protected]:username/second-repository.git
[email protected]:username/third-repository.git

Chúng tôi có thể tạo các kho lưu trữ này trên GitLab, thêm URL làm điều khiển từ xa và đẩy mã của chúng tôi sang các kho lưu trữ mới bằng cách chạy:

awk -F'\/|(\.git)' '{system("cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push")}' gl-repos.txt

Vui lòng đợi và tôi sẽ giải thích điều đó; bây giờ, hãy lưu ý rằng ~/FULL/PATH/ phải là đường dẫn đầy đủ đến thư mục chứa các kho lưu trữ GitHub của chúng tôi.

Chúng tôi phải lưu ý một số giả định:

  1. Tên của thư mục trên máy cục bộ của bạn có chứa kho lưu trữ giống với tên của kho lưu trữ trong URL (trường hợp này sẽ xảy ra nếu nó được sao chép với một lớp lót ở trên);
  2. Mỗi kho lưu trữ hiện được kiểm tra đến nhánh bạn muốn đẩy, tức là. master .

Một lớp lót có thể được mở rộng để xử lý những giả định này, nhưng theo ý kiến ​​khiêm tốn của tác giả rằng tại thời điểm đó, chúng tôi thực sự nên viết một tập lệnh Bash.

Chuyện gì đang xảy ra ở đây?

Bash one-liner của chúng tôi sử dụng từng dòng (hoặc URL) trong gl-repos.txt tệp làm đầu vào. Với awk , nó tách tên của thư mục chứa kho lưu trữ trên máy cục bộ của chúng tôi và sử dụng các phần thông tin này để xây dựng lệnh lớn hơn của chúng tôi. Nếu chúng ta print đầu ra của awk , chúng tôi sẽ thấy:

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

Hãy xem cách chúng tôi xây dựng lệnh này.

Tách chuỗi bằng awk

Công cụ awk có thể phân chia đầu vào dựa trên dấu tách trường. Dấu phân tách mặc định là một ký tự khoảng trắng, nhưng chúng ta có thể thay đổi điều này bằng cách chuyển -F lá cờ. Bên cạnh các ký tự đơn, chúng ta cũng có thể sử dụng dấu phân tách trường biểu thức chính quy. Vì URL kho lưu trữ của chúng tôi có định dạng đã đặt, chúng tôi có thể lấy tên kho lưu trữ bằng cách yêu cầu chuỗi con giữa ký tự gạch chéo / và phần cuối của URL, .git .

Một cách để thực hiện điều này là với regex \/|(\.git) của chúng tôi :

  • \/ là một / thoát nhân vật;
  • | có nghĩa là “hoặc”, cho biết awk khớp với một trong hai biểu thức;
  • (\.git) là nhóm nắm bắt ở cuối URL của chúng tôi khớp với “.git”, với một . thoát. tính cách. Đây là một chút gian lận, vì “.git” không hoàn toàn chia nhỏ bất kỳ thứ gì (không có gì ở phía bên kia) nhưng đó là một cách dễ dàng để chúng tôi gỡ bỏ điều này.

Khi chúng tôi đã nói với awk nơi cần tách, chúng ta có thể lấy đúng chuỗi con bằng toán tử trường. Chúng tôi tham chiếu đến các trường của mình bằng $ ký tự, sau đó là số cột của trường. Trong ví dụ của chúng tôi, chúng tôi muốn trường thứ hai, $2 . Đây là giao diện của tất cả các chuỗi con:

1: [email protected]:username
2: first-repository

Để sử dụng toàn bộ chuỗi hoặc trong trường hợp của chúng tôi là toàn bộ URL, chúng tôi sử dụng toán tử trường $0 . Để viết lệnh, chúng ta chỉ cần thay thế các toán tử trường cho tên và URL của kho lưu trữ. Chạy điều này với print vì chúng tôi đang xây dựng nó có thể giúp đảm bảo rằng chúng tôi đã có tất cả các không gian phù hợp.

awk -F'\/|(\.git)' '{print "cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push"}' gl-repos.txt

Chạy lệnh

Chúng tôi xây dựng lệnh của mình bên trong dấu ngoặc đơn của system() . Bằng cách sử dụng nó làm đầu ra của awk , mỗi lệnh sẽ chạy ngay khi nó được xây dựng và xuất ra. Hệ thống system() hàm tạo một tiến trình con thực thi lệnh của chúng ta, sau đó trả về sau khi lệnh hoàn thành. Bằng tiếng Anh đơn giản, điều này cho phép chúng tôi thực hiện các lệnh Git trên từng kho lưu trữ, từng cái một, mà không phá vỡ quy trình chính của chúng tôi, trong đó awk đang làm những việc với tệp đầu vào của chúng tôi. Đây là lệnh cuối cùng của chúng tôi một lần nữa, tất cả được tập hợp lại với nhau.

awk -F'\/|(\.git)' '{system("cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push")}' gl-repos.txt

Sử dụng các bản sao lưu của chúng tôi

Bằng cách thêm các URL GitLab làm điều khiển từ xa, chúng tôi đã đơn giản hóa quá trình đẩy sang cả các kho lưu trữ được lưu trữ bên ngoài. Nếu chúng ta chạy git remote -v trong một trong các thư mục kho lưu trữ của chúng tôi, chúng ta sẽ thấy:

origin  [email protected]:username/first-repository.git (fetch)
origin  [email protected]:username/first-repository.git (push)
origin  [email protected]:username/first-repository.git (push)

Bây giờ, chỉ cần chạy git push không có đối số sẽ đẩy nhánh hiện tại đến cả hai kho lưu trữ từ xa.

Chúng ta cũng nên lưu ý rằng git pull nói chung sẽ chỉ cố gắng lấy từ kho lưu trữ từ xa mà bạn đã sao chép ban đầu (URL được đánh dấu (fetch) trong ví dụ của chúng tôi ở trên). Việc kéo từ nhiều kho lưu trữ Git cùng một lúc là có thể, nhưng phức tạp và nằm ngoài phạm vi của bài đăng này. Dưới đây là giải thích về việc đẩy và kéo đến nhiều điều khiển từ xa để giúp bạn bắt đầu, nếu bạn tò mò. Tài liệu Git về điều khiển từ xa cũng có thể hữu ích.

Để giải thích kỹ hơn về tính ngắn gọn của Bash one-liners

Bash một-liners, khi được hiểu, có thể là những phím tắt thú vị và tiện dụng. Ít nhất, hãy lưu ý đến các công cụ như xargsawk có thể giúp tự động hóa và giảm bớt rất nhiều sự tẻ nhạt trong công việc của chúng tôi. Tuy nhiên, có một số nhược điểm.

Xét về một công cụ dễ hiểu, dễ bảo trì và dễ tiếp cận, Bash one-liners rất tệ. Chúng thường phức tạp hơn để viết so với tập lệnh Bash sử dụng if hoặc while vòng lặp, và chắc chắn phức tạp hơn để đọc. Có khả năng là khi viết chúng, chúng ta sẽ bỏ sót một câu trích dẫn hoặc dấu ngoặc đơn ở đâu đó; và như tôi hy vọng bài đăng này chứng minh, họ cũng có thể giải thích khá nhiều. Vậy tại sao lại sử dụng chúng?

Hãy tưởng tượng bạn đang đọc từng bước một công thức làm bánh. Bạn hiểu các phương pháp và thành phần, và thu thập nguồn cung cấp của bạn. Sau đó, khi bạn nghĩ về nó, bạn bắt đầu nhận ra rằng nếu bạn chỉ cần ném tất cả các nguyên liệu vào lò theo đúng thứ tự, một chiếc bánh sẽ ngay lập tức thành hiện thực. Bạn thử nó, và nó hoạt động!

Điều đó sẽ khá hài lòng, phải không?