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

Cách lập trình với Bash:Cú pháp và công cụ

Một trình bao là trình thông dịch lệnh cho hệ điều hành. Bash là trình bao yêu thích của tôi, nhưng mọi trình bao Linux đều diễn giải các lệnh được nhập bởi người dùng hoặc sysadmin thành một dạng mà hệ điều hành có thể sử dụng. Khi kết quả được trả về chương trình shell, nó sẽ gửi chúng đến STDOUT, theo mặc định, sẽ hiển thị chúng trong thiết bị đầu cuối. Tất cả các shell mà tôi quen thuộc cũng là ngôn ngữ lập trình.

Các tính năng như hoàn thành tab, gọi lại và chỉnh sửa dòng lệnh cũng như các phím tắt như bí danh đều góp phần nâng cao giá trị của nó như một lớp vỏ mạnh mẽ. Chế độ chỉnh sửa dòng lệnh mặc định của nó sử dụng Emacs, nhưng một trong những tính năng Bash yêu thích của tôi là tôi có thể thay đổi nó thành chế độ Vi để sử dụng các lệnh chỉnh sửa đã nằm trong bộ nhớ cơ của tôi.

Tuy nhiên, nếu bạn chỉ nghĩ về Bash như một cái vỏ, bạn đã bỏ lỡ phần lớn sức mạnh thực sự của nó. Trong khi nghiên cứu khóa học tự học Linux ba tập của mình (dựa trên loạt bài viết này), tôi đã học được những điều về Bash mà tôi chưa từng biết trong hơn 20 năm làm việc với Linux. Một số kiến ​​thức mới này liên quan đến việc sử dụng nó như một ngôn ngữ lập trình. 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 gồm ba phần này khám phá cách sử dụng Bash làm ngôn ngữ lập trình giao diện dòng lệnh (CLI). Bài viết đầu tiên này xem xét một số lập trình dòng lệnh đơn giản với Bash, các biến và toán tử điều khiển. Các bài viết khác khám phá các loại tệp Bash; các toán tử logic chuỗi, số và linh tinh cung cấp logic điều khiển luồng thực thi; các loại mở rộng vỏ khác nhau; và cho , trong khi cho đến khi vòng lặp cho phép các hoạt động lặp lại. Họ cũng sẽ xem xét một số lệnh đơn giản hóa và hỗ trợ việc sử dụng các công cụ này.

Vỏ

Một trình bao là trình thông dịch lệnh cho hệ điều hành. Bash là trình bao yêu thích của tôi, nhưng mọi trình bao Linux đều diễn giải các lệnh được nhập bởi người dùng hoặc sysadmin thành một dạng mà hệ điều hành có thể sử dụng. Khi kết quả được trả về chương trình shell, nó sẽ hiển thị chúng trong thiết bị đầu cuối. Tất cả các shell mà tôi quen thuộc cũng là ngôn ngữ lập trình.

Bash là viết tắt của Bourne Again Shell bởi vì Bash shell dựa trên Bourne shell cũ hơn được viết bởi Steven Bourne vào năm 1977. Có nhiều loại shell khác, nhưng đây là bốn loại mà tôi gặp thường xuyên nhất:

  • csh: C shell dành cho những lập trình viên thích cú pháp của ngôn ngữ C
  • ksh: The Korn shell, do David Korn viết và được người dùng Unix ưa chuộng
  • tcsh: Một phiên bản csh với nhiều tính năng dễ sử dụng hơn
  • zsh: Vỏ Z, kết hợp nhiều tính năng của các trình bao phổ biến khác

Tất cả các shell đều có các lệnh tích hợp để bổ sung hoặc thay thế các lệnh được cung cấp bởi các tiện ích cốt lõi. Mở man page của shell và tìm phần "BUILT-INS" để xem các lệnh mà nó cung cấp.

Mỗi shell có tính cách và cú pháp riêng. Một số sẽ làm việc tốt hơn cho bạn so với những người khác. Tôi đã sử dụng vỏ C, vỏ Korn và vỏ Z. Tôi vẫn thích Bash shell hơn bất kỳ cái nào trong số chúng. Sử dụng cái phù hợp nhất với bạn, mặc dù điều đó có thể yêu cầu bạn thử một số cái khác. May mắn thay, việc thay đổi vỏ khá dễ dàng.

Tất cả các shell này đều là ngôn ngữ lập trình, cũng như trình thông dịch lệnh. Dưới đây là phần giới thiệu nhanh về một số cấu trúc và công cụ lập trình là những phần không thể thiếu của Bash.

Bash làm ngôn ngữ lập trình

Hầu hết các sysadmins đã sử dụng Bash để đưa ra các lệnh thường khá đơn giản và dễ hiểu. Nhưng Bash có thể vượt xa việc nhập các lệnh đơn lẻ, và nhiều sysadmins tạo ra các chương trình dòng lệnh đơn giản để thực hiện một loạt tác vụ. Các chương trình này là công cụ phổ biến có thể tiết kiệm thời gian và công sức.

Mục tiêu của tôi khi viết các chương trình CLI là tiết kiệm thời gian và công sức (tức là trở thành một sysadmin lười biếng). Các chương trình CLI hỗ trợ điều này bằng cách liệt kê một số lệnh trong một trình tự cụ thể thực hiện lần lượt, vì vậy bạn không cần phải theo dõi tiến trình của một lệnh và nhập lệnh tiếp theo khi lệnh đầu tiên kết thúc. Bạn có thể làm những việc khác và không phải liên tục theo dõi tiến trình của từng lệnh.

"Chương trình" là gì?

Từ điển Máy tính Trực tuyến Miễn phí (FOLDOC) định nghĩa một chương trình là:"Các hướng dẫn được thực thi bởi một máy tính, trái ngược với thiết bị vật lý mà chúng chạy trên đó." Mạng từ của Đại học Princeton định nghĩa một chương trình là:"… một chuỗi hướng dẫn mà máy tính có thể diễn giải và thực thi ..." Wikipedia cũng có một mục hay về các chương trình máy tính.

Do đó, một chương trình có thể bao gồm một hoặc nhiều lệnh thực hiện một nhiệm vụ cụ thể, có liên quan. Một lệnh chương trình máy tính còn được gọi là một câu lệnh chương trình. Đối với sysadmins, một chương trình thường là một chuỗi các lệnh shell. Tất cả các shell có sẵn cho Linux, ít nhất là những cái mà tôi quen thuộc, có ít nhất một dạng cơ bản của khả năng lập trình và Bash, shell mặc định cho hầu hết các bản phân phối Linux, cũng không ngoại lệ.

Trong khi loạt bài này sử dụng Bash (vì nó rất phổ biến), nếu bạn sử dụng một trình bao khác, các khái niệm lập trình chung sẽ giống nhau, mặc dù cấu trúc và cú pháp có thể khác nhau đôi chút. Một số shell có thể hỗ trợ một số tính năng mà những shell khác thì không, nhưng chúng đều cung cấp một số khả năng lập trình. Các chương trình shell có thể được lưu trữ trong một tệp để sử dụng nhiều lần hoặc chúng có thể được tạo trên dòng lệnh khi cần thiết.

Các chương trình CLI đơn giản

Các chương trình dòng lệnh đơn giản nhất là một hoặc hai câu lệnh chương trình liên tiếp, có thể liên quan hoặc không, được nhập trên dòng lệnh trước Enter phím được nhấn. Câu lệnh thứ hai trong một chương trình, nếu có, có thể phụ thuộc vào các hành động của câu lệnh đầu tiên, nhưng nó không cần phải như vậy.

Cũng có một số dấu câu cú pháp cần được nêu rõ ràng. Khi nhập một lệnh duy nhất trên dòng lệnh, nhấn Enter phím kết thúc lệnh bằng dấu chấm phẩy ngầm (; ). Khi được sử dụng trong chương trình shell CLI được nhập dưới dạng một dòng trên dòng lệnh, dấu chấm phẩy phải được sử dụng để kết thúc mỗi câu lệnh và tách nó khỏi câu lệnh tiếp theo. Câu lệnh cuối cùng trong chương trình shell CLI có thể sử dụng dấu chấm phẩy rõ ràng hoặc ẩn.

Một số cú pháp cơ bản

Các ví dụ sau đây sẽ làm rõ cú pháp này. Chương trình này bao gồm một lệnh duy nhất với một dấu chấm cuối rõ ràng:

[student@studentvm1 ~]$ echo "Hello world." ;
Hello world.

Đó có vẻ không giống một chương trình, nhưng nó là chương trình đầu tiên tôi gặp với mọi ngôn ngữ lập trình mới mà tôi học. Cú pháp có thể khác một chút đối với mỗi ngôn ngữ, nhưng kết quả là như nhau.

Hãy mở rộng một chút về chương trình tầm thường nhưng phổ biến này. Kết quả của bạn sẽ khác với kết quả của tôi vì tôi đã thực hiện các thử nghiệm khác, trong khi bạn có thể chỉ có các thư mục và tệp mặc định được tạo trong thư mục chính của tài khoản trong lần đầu tiên bạn đăng nhập vào tài khoản thông qua màn hình GUI.

[student@studentvm1 ~]$ echo "My home directory." ; ls ;
My home directory.
chapter25   TestFile1.Linux  dmesg2.txt  Downloads  newfile.txt  softlink1  testdir6
chapter26   TestFile1.mac    dmesg3.txt  file005    Pictures     Templates  testdir
TestFile1      Desktop       dmesg.txt   link3      Public       testdir    Videos
TestFile1.dos  dmesg1.txt    Documents   Music      random.txt   testdir1

Điều đó có ý nghĩa hơn một chút. Các kết quả có liên quan với nhau, nhưng các câu lệnh chương trình riêng lẻ độc lập với nhau. Lưu ý rằng tôi thích đặt khoảng trắng trước và sau dấu chấm phẩy vì nó làm cho mã dễ đọc hơn một chút. Hãy thử lại chương trình CLI nhỏ đó mà không có dấu chấm phẩy rõ ràng ở cuối:

[student@studentvm1 ~]$ echo "My home directory." ; ls 

Không có sự khác biệt trong đầu ra.

Vài nét về các biến

Giống như tất cả các ngôn ngữ lập trình, Bash shell có thể xử lý các biến. Một biến là một tên tượng trưng dùng để chỉ một vị trí cụ thể trong bộ nhớ có chứa một giá trị nào đó. Giá trị của một biến có thể thay đổi, tức là nó có thể thay đổi được.

Bash không nhập các biến như C và các ngôn ngữ liên quan, định nghĩa chúng dưới dạng số nguyên, dấu phẩy động hoặc kiểu chuỗi. Trong Bash, tất cả các biến đều là chuỗi. Một chuỗi là một số nguyên có thể được sử dụng trong số học số nguyên, đây là loại toán duy nhất mà Bash có khả năng thực hiện. Nếu yêu cầu toán học phức tạp hơn, thì bc lệnh có thể được sử dụng trong các chương trình và tập lệnh CLI.

Các biến là các giá trị được gán và có thể được sử dụng để tham chiếu đến các giá trị đó trong các chương trình và tập lệnh CLI. Giá trị của một biến được đặt bằng cách sử dụng tên của nó nhưng không được đặt trước bằng $ dấu hiệu. Phép gán VAR =10 đặt giá trị của biến VAR thành 10. Để in giá trị của biến, bạn có thể sử dụng câu lệnh echo $ VAR . Bắt đầu với các biến văn bản (tức là không phải số).

Các biến Bash trở thành một phần của môi trường shell cho đến khi chúng không được đặt.

Kiểm tra giá trị ban đầu của một biến chưa được gán; nó phải là null. Sau đó, gán một giá trị cho biến và in ra để xác minh giá trị của nó. Bạn có thể thực hiện tất cả những điều này trong một chương trình CLI duy nhất:

[student@studentvm1 ~]$ echo $MyVar ; MyVar="Hello World" ; echo $MyVar ;

Hello World
[student@studentvm1 ~]$

Lưu ý:Cú pháp của phép gán biến rất nghiêm ngặt. Không được có khoảng trắng ở hai bên của dấu bằng ( = ) đăng nhập vào câu lệnh gán.

Dòng trống cho biết rằng giá trị ban đầu của MyVar là null. Việc thay đổi và thiết lập giá trị của một biến được thực hiện theo cùng một cách. Ví dụ này hiển thị cả giá trị ban đầu và giá trị mới.

Như đã đề cập, Bash có thể thực hiện các phép tính số học số nguyên, rất hữu ích để tính toán tham chiếu đến vị trí của một phần tử trong một mảng hoặc thực hiện các bài toán đơn giản. Nó không phù hợp với tính toán khoa học hoặc bất cứ thứ gì yêu cầu số thập phân, chẳng hạn như tính toán tài chính. Có nhiều công cụ tốt hơn cho các loại tính toán đó.

Đây là một phép tính đơn giản:

[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = $((Var1*Var2))"
Result = 63

Điều gì xảy ra khi bạn thực hiện một phép toán dẫn đến một số dấu phẩy động?

[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = $((Var1/Var2))"
Result = 0
[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = $((Var2/Var1))"
Result = 1
[student@studentvm1 ~]$

Kết quả là số nguyên gần nhất. Lưu ý rằng phép tính được thực hiện như một phần của echo bản tường trình. Phép toán được thực hiện trước lệnh echo kèm theo do thứ tự ưu tiên Bash. Để biết chi tiết, hãy xem trang Bash man và tìm kiếm "mức độ ưu tiên".

Toán tử điều khiển

Toán tử điều khiển shell là một trong những toán tử cú pháp để dễ dàng tạo một số chương trình dòng lệnh thú vị. Hình thức đơn giản nhất của chương trình CLI chỉ là xâu chuỗi một số lệnh lại với nhau thành một chuỗi trên dòng lệnh:

command1 ; command2 ; command3 ; command4 ; . . . ; etc. ;

Tất cả các lệnh đó đều chạy mà không có vấn đề gì miễn là không có lỗi xảy ra. Nhưng điều gì sẽ xảy ra khi xảy ra lỗi? Bạn có thể đoán trước và xử lý lỗi bằng cách sử dụng && tích hợp sẵn và || Các toán tử điều khiển bash. Hai toán tử điều khiển này cung cấp một số điều khiển luồng và cho phép bạn thay đổi trình tự thực thi mã. Dấu chấm phẩy cũng được coi là một toán tử điều khiển Bash, cũng như ký tự dòng mới.

&& toán tử chỉ đơn giản nói, "nếu command1 thành công, thì hãy chạy command2. Nếu command1 không thành công vì bất kỳ lý do gì, thì command2 sẽ bị bỏ qua." Cú pháp đó trông giống như sau:

command1 && command2

Bây giờ, hãy xem một số lệnh sẽ tạo một thư mục mới và - nếu nó thành công - hãy đặt nó trở thành thư mục làm việc hiện tại (PWD). Đảm bảo rằng thư mục chính của bạn ( ~ ) là NKT. Trước tiên, hãy thử điều này trong / root , một thư mục mà bạn không có quyền truy cập vào:

[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir/ && cd $Dir
mkdir: cannot create directory '/root/testdir/': Permission denied
[student@studentvm1 ~]$

Lỗi do mkdir tạo ra yêu cầu. Bạn không nhận được lỗi cho biết rằng không thể tạo tệp do việc tạo thư mục không thành công. && người điều khiển đã cảm nhận được mã trả về khác 0, vì vậy hãy chạm lệnh đã bị bỏ qua. Sử dụng && người điều khiển ngăn chạm lệnh chạy vì đã xảy ra lỗi khi tạo thư mục. Loại điều khiển luồng chương trình dòng lệnh này có thể ngăn chặn lỗi từ việc ghép lại và tạo ra một mớ hỗn độn thực sự. Nhưng đã đến lúc phức tạp hơn một chút.

|| toán tử điều khiển cho phép bạn thêm một câu lệnh chương trình khác thực thi khi câu lệnh chương trình ban đầu trả về mã lớn hơn 0. Cú pháp cơ bản giống như sau:

command1 || command2 

Cú pháp này có nội dung, "Nếu command1 không thành công, hãy thực thi command2." Điều đó ngụ ý rằng nếu command1 thành công, command2 sẽ bị bỏ qua. Hãy thử điều này bằng cách cố gắng tạo một thư mục mới:

[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir || echo "$Dir was not created."
mkdir: cannot create directory '/root/testdir': Permission denied
/root/testdir was not created.
[student@studentvm1 ~]$

Đây chính xác là những gì bạn mong đợi. Vì không thể tạo thư mục mới, lệnh đầu tiên không thành công, dẫn đến việc thực thi lệnh thứ hai.

Kết hợp hai toán tử này cung cấp những gì tốt nhất của cả hai. Cú pháp của toán tử điều khiển sử dụng một số điều khiển luồng có dạng chung này khi && || các toán tử điều khiển được sử dụng:

Các lệnh trước
preceding commands ; command1 && command2 || command3 ; following commands

Cú pháp này có thể được phát biểu như sau:"Nếu command1 thoát với mã trả về 0, thì thực hiện command2, ngược lại thực hiện command3." Hãy thử nó:

[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
mkdir: cannot create directory '/root/testdir': Permission denied
/root/testdir was not created.
[student@studentvm1 ~]$

Bây giờ, hãy thử lại lệnh cuối cùng bằng cách sử dụng thư mục chính của bạn thay vì / root danh mục. Bạn sẽ có quyền tạo thư mục này:

[student@studentvm1 ~]$ Dir=~/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
[student@studentvm1 testdir]$

Cú pháp của toán tử điều khiển, như command1 &&command2 , hoạt động bởi vì mọi lệnh đều gửi một mã trả về (RC) tới trình bao cho biết nó đã hoàn thành thành công hay chưa hoặc có một số loại lỗi nào đó trong quá trình thực thi hay không. Theo quy ước, RC bằng không (0) cho biết thành công và bất kỳ số dương nào cho biết một số loại thất bại. Một số công cụ mà sysadmins sử dụng chỉ trả về một (1) để cho biết lỗi, nhưng nhiều công cụ sử dụng các mã khác để chỉ ra loại lỗi đã xảy ra.

Biến trình bao Bash $? chứa RC từ lệnh cuối cùng. RC này có thể được kiểm tra rất dễ dàng bằng script, lệnh tiếp theo trong danh sách lệnh hoặc thậm chí sysadmin trực tiếp. Bắt đầu bằng cách chạy một lệnh đơn giản và ngay lập tức kiểm tra RC. RC sẽ luôn dành cho lệnh cuối cùng chạy trước khi bạn nhìn vào nó.

[student@studentvm1 testdir]$ ll ; echo "RC = $?"
total 1264
drwxrwxr-x  2 student student   4096 Mar  2 08:21 chapter25
drwxrwxr-x  2 student student   4096 Mar 21 15:27 chapter26
-rwxr-xr-x  1 student student     92 Mar 20 15:53 TestFile1
<snip>
drwxrwxr-x. 2 student student 663552 Feb 21 14:12 testdir
drwxr-xr-x. 2 student student   4096 Dec 22 13:15 Videos
RC = 0
[student@studentvm1 testdir]$

RC, trong trường hợp này, là 0, có nghĩa là lệnh đã hoàn thành thành công. Bây giờ hãy thử lệnh tương tự trên thư mục gốc của thư mục gốc, thư mục mà bạn không có quyền:

[student@studentvm1 testdir]$ ll /root ; echo "RC = $?"
ls: cannot open directory '/root': Permission denied
RC = 2
[student@studentvm1 testdir]$

Trong trường hợp này, RC là hai; điều này có nghĩa là quyền đã bị từ chối đối với người dùng không phải root để truy cập vào thư mục mà người dùng không được phép truy cập. Các toán tử điều khiển sử dụng các RC này để cho phép bạn thay đổi trình tự thực hiện chương trình.

Tóm tắt

Bài viết này đã xem xét Bash như một ngôn ngữ lập trình và khám phá cú pháp cơ bản của nó cũng như một số công cụ cơ bản. Nó chỉ ra cách in dữ liệu ra STDOUT và cách sử dụng các biến và toán tử điều khiển. Bài tiếp theo của loạt bài này xem xét một số toán tử logic Bash điều khiển luồng thực thi lệnh.