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

Cách lập trình với Bash:Loops

Bash là một ngôn ngữ lập trình mạnh mẽ, được thiết kế hoàn hảo để sử dụng trên dòng lệnh và trong các tập lệnh shell. Loạt bài ba phần này, dựa trên khóa học tự học Linux ba tập của tôi, khám phá cách sử dụng Bash làm ngôn ngữ lập trình trên giao diện dòng lệnh (CLI).

Bài đầu tiên của loạt bài này đã khám phá một số lập trình dòng lệnh đơn giản với Bash, bao gồm cả việc sử dụng các biến và toán tử điều khiển. Bài viết thứ hai xem xét các loại toán tử logic tệp, chuỗi, số và linh tinh cung cấp logic điều khiển luồng thực thi và các loại mở rộng shell khác nhau trong Bash. Bài viết thứ ba (và cuối cùng) này kiểm tra việc sử dụng các vòng lặp để thực hiện các loại hoạt động lặp khác nhau và các cách để kiểm soát các vòng lặp đó.

Vòng lặp

Mọi ngôn ngữ lập trình tôi từng sử dụng đều có ít nhất một vài kiểu cấu trúc vòng lặp cung cấp các khả năng khác nhau để thực hiện các hoạt động lặp lại. Tôi sử dụng vòng lặp for khá thường xuyên nhưng tôi cũng thấy các vòng lặp while và cho đến khi hữu ích.

cho các vòng lặp

Việc triển khai for của Bash Theo tôi, lệnh linh hoạt hơn một chút vì nó có thể xử lý các giá trị không phải là số; ngược lại, ví dụ:ngôn ngữ C chuẩn for vòng lặp chỉ có thể xử lý các giá trị số.

Cấu trúc cơ bản của phiên bản Bash của for lệnh rất đơn giản:

for Var in list1 ; do list2 ; done

Điều này có nghĩa là:"Đối với mỗi giá trị trong list1, hãy đặt $ Var đến giá trị đó và sau đó thực hiện các câu lệnh chương trình trong list2 bằng cách sử dụng giá trị đó; khi tất cả các giá trị trong list1 đã được sử dụng hết, vì vậy hãy thoát khỏi vòng lặp. "Các giá trị trong list1 có thể là một chuỗi giá trị rõ ràng, đơn giản hoặc chúng có thể là kết quả của một phép thay thế lệnh (được mô tả trong phần thứ hai trong loạt bài). Tôi sử dụng cấu trúc này thường xuyên.

Để thử, hãy đảm bảo rằng ~ / testdir vẫn là thư mục làm việc hiện tại (PWD). Dọn dẹp thư mục, sau đó xem một ví dụ đơn giản về for vòng lặp bắt đầu với một danh sách các giá trị rõ ràng. Danh sách này là sự kết hợp của các giá trị chữ và số — nhưng đừng quên rằng tất cả các biến đều là chuỗi và có thể được xử lý như vậy.

[student@studentvm1 testdir]$ rm *
[student@studentvm1 testdir]$ for I in a b c d 1 2 3 4 ; do echo $I ; done
a
b
c
d
1
2
3
4

Đây là phiên bản hữu ích hơn một chút với tên biến có ý nghĩa hơn:

[student@studentvm1 testdir]$ for Dept in "Human Resources" Sales Finance "Information Technology" Engineering Administration Research ; do echo "Department $Dept" ; done 
Department Human Resources
Department Sales
Department Finance
Department Information Technology
Department Engineering
Department Administration
Department Research

Tạo một số thư mục (và hiển thị một số thông tin tiến trình trong khi làm như vậy):

[student@studentvm1 testdir]$ for Dept in "Human Resources" Sales Finance "Information Technology" Engineering Administration Research ; do echo "Working on Department $Dept" ; mkdir "$Dept"  ; done 
Working on Department Human Resources
Working on Department Sales
Working on Department Finance
Working on Department Information Technology
Working on Department Engineering
Working on Department Administration
Working on Department Research
[student@studentvm1 testdir]$ ll
total 28
drwxrwxr-x 2 student student 4096 Apr  8 15:45  Administration
drwxrwxr-x 2 student student 4096 Apr  8 15:45  Engineering
drwxrwxr-x 2 student student 4096 Apr  8 15:45  Finance
drwxrwxr-x 2 student student 4096 Apr  8 15:45 'Human Resources'
drwxrwxr-x 2 student student 4096 Apr  8 15:45 'Information Technology'
drwxrwxr-x 2 student student 4096 Apr  8 15:45  Research
drwxrwxr-x 2 student student 4096 Apr  8 15:45  Sales

Khoản tiền gửi $ biến phải được đặt trong dấu ngoặc kép trong mkdir bản tường trình; nếu không, tên bộ phận gồm hai bộ phận (chẳng hạn như "Công nghệ thông tin") sẽ được coi là hai bộ phận riêng biệt. Điều đó làm nổi bật một phương pháp hay nhất mà tôi muốn làm theo:tất cả các tên tệp và thư mục phải là một từ duy nhất. Mặc dù hầu hết các hệ điều hành hiện đại có thể xử lý các khoảng trắng trong tên, các sysadmins phải làm việc thêm để đảm bảo rằng các trường hợp đặc biệt đó được xem xét trong các tập lệnh và chương trình CLI. (Chúng gần như chắc chắn nên được xem xét, ngay cả khi chúng gây phiền nhiễu vì bạn không bao giờ biết mình sẽ có những tệp nào.)

Vì vậy, hãy xóa mọi thứ trong ~ / testdir —Cũng được — và làm điều này một lần nữa:

[student@studentvm1 testdir]$ rm -rf * ; ll
total 0
[student@studentvm1 testdir]$ for Dept in Human-Resources Sales Finance Information-Technology Engineering Administration Research ; do echo "Working on Department $Dept" ; mkdir "$Dept"  ; done
Working on Department Human-Resources
Working on Department Sales
Working on Department Finance
Working on Department Information-Technology
Working on Department Engineering
Working on Department Administration
Working on Department Research
[student@studentvm1 testdir]$ ll
total 28
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Administration
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Engineering
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Finance
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Human-Resources
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Information-Technology
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Research
drwxrwxr-x 2 student student 4096 Apr  8 15:52 Sales

Giả sử ai đó yêu cầu danh sách tất cả RPM trên một máy tính Linux cụ thể và mô tả ngắn gọn về mỗi RPM. Điều này đã xảy ra với tôi khi tôi làm việc cho Bang Bắc Carolina. Vì mã nguồn mở không được các cơ quan nhà nước "cho phép" sử dụng vào thời điểm đó và tôi chỉ sử dụng Linux trên máy tính để bàn của mình, nên các ông chủ đầu nhọn (PHB) cần một danh sách từng phần mềm đã được cài đặt trên máy tính của tôi. rằng họ có thể "chấp thuận" một ngoại lệ.

Bạn sẽ tiếp cận điều đó như thế nào? Đây là một cách, bắt đầu với kiến ​​thức rằng rpm –qa lệnh cung cấp mô tả đầy đủ về RPM, bao gồm hai mục mà PHB muốn:tên phần mềm và tóm tắt ngắn gọn.

Xây dựng đến kết quả cuối cùng từng bước một. Đầu tiên, liệt kê tất cả RPM:

[student@studentvm1 testdir]$ rpm -qa 
perl-HTTP-Message-6.18-3.fc29.noarch
perl-IO-1.39-427.fc29.x86_64
perl-Math-Complex-1.59-429.fc29.noarch
lua-5.3.5-2.fc29.x86_64
java-11-openjdk-headless-11.0.ea.28-2.fc29.x86_64
util-linux-2.32.1-1.fc29.x86_64
libreport-fedora-2.9.7-1.fc29.x86_64
rpcbind-1.2.5-0.fc29.x86_64
libsss_sudo-2.0.0-5.fc29.x86_64
libfontenc-1.1.3-9.fc29.x86_64
<snip>

Thêm sắp xếp uniq các lệnh để sắp xếp danh sách và in các lệnh duy nhất (vì có thể một số RPM có tên giống nhau được cài đặt):

[student@studentvm1 testdir]$ rpm -qa | sort | uniq
a2ps-4.14-39.fc29.x86_64
aajohan-comfortaa-fonts-3.001-3.fc29.noarch
abattis-cantarell-fonts-0.111-1.fc29.noarch
abiword-3.0.2-13.fc29.x86_64
abrt-2.11.0-1.fc29.x86_64
abrt-addon-ccpp-2.11.0-1.fc29.x86_64
abrt-addon-coredump-helper-2.11.0-1.fc29.x86_64
abrt-addon-kerneloops-2.11.0-1.fc29.x86_64
abrt-addon-pstoreoops-2.11.0-1.fc29.x86_64
abrt-addon-vmcore-2.11.0-1.fc29.x86_64
<snip>

Vì điều này cung cấp danh sách chính xác các RPM mà bạn muốn xem, bạn có thể sử dụng danh sách này làm danh sách đầu vào cho một vòng lặp sẽ in tất cả các chi tiết của từng RPM:

[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done

Mã này tạo ra nhiều dữ liệu hơn bạn muốn. Lưu ý rằng vòng lặp đã hoàn tất. Bước tiếp theo là chỉ trích xuất thông tin mà PHB đã yêu cầu. Vì vậy, hãy thêm một egrep , được sử dụng để chọn ^ Tên hoặc ^ Tóm tắt . Carat ( ^ ) xác định đầu dòng; do đó, bất kỳ dòng nào có Tên hoặc Tóm tắt ở đầu dòng sẽ được hiển thị.

[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done | egrep -i "^Name|^Summary"
Name        : a2ps
Summary     : Converts text and other types of files to PostScript
Name        : aajohan-comfortaa-fonts
Summary     : Modern style true type font
Name        : abattis-cantarell-fonts
Summary     : Humanist sans serif font
Name        : abiword
Summary     : Word processing program
Name        : abrt
Summary     : Automatic bug detection and reporting tool
<snip>

Bạn có thể thử grep thay vì egrep trong lệnh trên, nhưng nó sẽ không hoạt động. Bạn cũng có thể chuyển đầu ra của lệnh này thông qua less bộ lọc để khám phá kết quả. Chuỗi lệnh cuối cùng trông giống như sau:

[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done | egrep -i "^Name|^Summary" > RPM-summary.txt

Chương trình dòng lệnh này sử dụng đường ống dẫn, chuyển hướng và cho vòng lặp — tất cả trên một dòng. Nó chuyển hướng đầu ra của chương trình CLI nhỏ của bạn thành một tệp có thể được sử dụng trong email hoặc làm đầu vào cho các mục đích khác.

Quá trình xây dựng chương trình từng bước một này cho phép bạn xem kết quả của từng bước và đảm bảo rằng chương trình đang hoạt động như bạn mong đợi và mang lại kết quả mong muốn.

Từ bài tập này, các PHB đã nhận được danh sách hơn 1.900 gói RPM riêng biệt. Tôi thực sự nghi ngờ rằng có ai đọc danh sách đó. Nhưng tôi đã cung cấp cho họ chính xác những gì họ yêu cầu, và tôi chưa bao giờ nghe họ nói thêm một lời nào về điều đó.

Các vòng lặp khác

Có hai loại cấu trúc vòng lặp khác có sẵn trong Bash: while cho đến khi cấu trúc, rất giống nhau về cả cú pháp và chức năng. Cú pháp cơ bản của các cấu trúc vòng lặp này rất đơn giản:

while [ expression ] ; do list ; done

until [ expression ] ; do list ; done

Logic của lần đầu tiên đọc:"Trong khi biểu thức đánh giá là đúng, hãy thực hiện danh sách các câu lệnh của chương trình. Khi biểu thức đánh giá là sai, hãy thoát khỏi vòng lặp." Và câu thứ hai:"Cho đến khi biểu thức đánh giá là true, hãy thực hiện danh sách các câu lệnh của chương trình. Khi biểu thức đánh giá là true, hãy thoát khỏi vòng lặp."

Vòng lặp trong khi

Trong khi vòng lặp được sử dụng để thực hiện một loạt các câu lệnh chương trình trong khi (miễn là) biểu thức logic đánh giá là đúng. NKT của bạn vẫn phải là ~ / testdir .

Dạng đơn giản nhất của while vòng lặp là một vòng lặp chạy mãi mãi. Biểu mẫu sau sử dụng câu lệnh true để luôn tạo mã trả về "true". Bạn cũng có thể sử dụng "1" đơn giản — và điều đó sẽ hoạt động giống nhau — nhưng điều này minh họa việc sử dụng câu lệnh true:

[student@studentvm1 testdir]$ X=0 ; while [ true ] ; do echo $X ; X=$((X+1)) ; done | head
0
1
2
3
4
5
6
7
8
9
[student@studentvm1 testdir]$

Chương trình CLI này sẽ có ý nghĩa hơn bây giờ khi bạn đã nghiên cứu các phần của nó. Đầu tiên, nó đặt $ X về 0 trong trường hợp nó còn dư giá trị từ chương trình trước đó hoặc lệnh CLI. Sau đó, vì biểu thức logic [true] luôn đánh giá là 1, đúng, danh sách hướng dẫn chương trình giữa do xong được thực thi mãi mãi — hoặc cho đến khi bạn nhấn Ctrl + C hoặc gửi tín hiệu 2 đến chương trình. Các hướng dẫn đó là một mở rộng số học in ra giá trị hiện tại của $ X và sau đó tăng dần từng thứ một.

Một trong những nguyên lý của Triết lý Linux cho Sysadmins là phấn đấu cho sự thanh lịch, và một cách để đạt được sự thanh lịch là sự đơn giản. Bạn có thể đơn giản hóa chương trình này bằng cách sử dụng toán tử gia tăng biến, ++ . Trong trường hợp đầu tiên, giá trị hiện tại của biến được in, và sau đó biến được tăng dần lên. Điều này được chỉ ra bằng cách đặt ++ toán tử sau biến:

[student@studentvm1 ~]$ X=0 ; while [ true ] ; do echo $((X++)) ; done | head
0
1
2
3
4
5
6
7
8
9

Bây giờ xóa | đầu từ cuối chương trình và chạy lại.

Trong phiên bản này, biến được tăng lên trước khi giá trị của nó được in. Điều này được chỉ định bằng cách đặt ++ toán tử trước biến. Bạn có thể thấy sự khác biệt không?

[student@studentvm1 ~]$ X=0 ; while [ true ] ; do echo $((++X)) ; done | head
1
2
3
4
5
6
7
8
9

Bạn đã giảm hai câu lệnh thành một câu lệnh duy nhất in ra giá trị của biến và tăng giá trị đó. Ngoài ra còn có một toán tử giảm dần, - .

Bạn cần một phương pháp để dừng vòng lặp ở một số cụ thể. Để thực hiện điều đó, hãy thay đổi biểu thức true thành một biểu thức đánh giá số thực. Có vòng lặp chương trình đến 5 và dừng lại. Trong mã ví dụ bên dưới, bạn có thể thấy rằng -le là toán tử số logic cho "nhỏ hơn hoặc bằng." Điều này có nghĩa là:"Miễn là $ X nhỏ hơn hoặc bằng 5, vòng lặp sẽ tiếp tục. Khi $ X tăng lên 6, vòng lặp kết thúc. "

[student@studentvm1 ~]$ X=0 ; while [ $X -le 5 ] ; do echo $((X++)) ; done 
0
1
2
3
4
5
[student@studentvm1 ~]$

Vòng lặp cho đến khi

Cho đến khi rất giống lệnh while yêu cầu. Sự khác biệt là nó sẽ tiếp tục lặp cho đến khi biểu thức logic đánh giá là "true". Hãy xem dạng đơn giản nhất của cấu trúc này:

[student@studentvm1 ~]$ X=0 ; until false  ; do echo $((X++)) ; done | head
0
1
2
3
4
5
6
7
8
9
[student@studentvm1 ~]$

Nó sử dụng một phép so sánh logic để đếm đến một giá trị cụ thể:

[student@studentvm1 ~]$ X=0 ; until [ $X -eq 5 ]  ; do echo $((X++)) ; done
0
1
2
3
4
[student@studentvm1 ~]$ X=0 ; until [ $X -eq 5 ]  ; do echo $((++X)) ; done
1
2
3
4
5
[student@studentvm1 ~]$

Tóm tắt

Loạt bài này đã khám phá nhiều công cụ mạnh mẽ để xây dựng các chương trình dòng lệnh Bash và các tập lệnh shell. Nhưng nó hầu như không làm trầy xước bề mặt trong nhiều điều thú vị mà bạn có thể làm với Bash; phần còn lại là tùy thuộc vào bạn.

Tôi đã phát hiện ra rằng cách tốt nhất để học lập trình Bash là làm điều đó. Tìm một dự án đơn giản yêu cầu nhiều lệnh Bash và tạo một chương trình CLI từ chúng. Sysadmins thực hiện nhiều tác vụ có lợi cho lập trình CLI, vì vậy tôi chắc chắn rằng bạn sẽ dễ dàng tìm thấy các tác vụ để tự động hóa.

Nhiều năm trước, mặc dù đã quen thuộc với các ngôn ngữ shell khác và Perl, tôi đã quyết định sử dụng Bash cho tất cả các nhiệm vụ tự động hóa sysadmin của mình. Tôi đã phát hiện ra rằng — đôi khi với một chút tìm kiếm — tôi đã có thể sử dụng Bash để hoàn thành mọi thứ mình cần.