Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Web Development
Code

Những nguyên tắc cơ bản của Bash Scripting

by
Difficulty:BeginnerLength:LongLanguages:

Vietnamese (Tiếng Việt) translation by Andrea Ho (you can also view the original English article)

Shell script được sử dụng rộng rãi trong thế giới UNIX. Chúng rất tuyệt vời để tăng tốc các tác vụ lặp đi lặp lại và đơn giản hóa logic thực thi phức tạp. Chúng có thể đơn giản như nhóm lệnh hoặc có thể phối hợp các tác vụ phức tạp. Trong hướng dẫn này, chúng ta sẽ tìm hiểu thêm về Bash script bằng cách từng bước viết một script ví dụ.


Vấn đề Fizz-Buzz

Một trong những cách tốt nhất để tìm hiểu về một ngôn ngữ mới là thông qua ví dụ. Hãy bắt đầu với một ví dụ.

Fizz-Buzz là một vấn đề rất đơn giản. Nó nổi tiếng sau khi một lập trình viên, tên là Imran, sử dụng nó như một bài kiểm tra phỏng vấn. Nó chỉ ra rằng 90-99,5% ứng viên của công việc lập trình đơn giản là không thể viết chương trình đơn giản nhất. Imran dùng game Fizz-Buzz đơn giản này và yêu cầu các ứng viên giải quyết nó. Nhiều người đã làm theo ví dụ của Imran, và, ngày nay, đó là một trong những câu hỏi thường gặp nhất cho công việc lập trình. Nếu bạn đang tuyển dụng và cần lọc qua 90% ứng viên, đây là một vấn đề tuyệt vời để áp dụng.

Đây là các quy tắc:

  • Lấy và in các số từ 1 đến 100.
  • Khi một số chia hết cho 3, hãy in "Fizz" thay vì số đó.
  • Khi nó chia hết cho 5, hãy in "Buzz" thay thế vào.
  • Khi nó chia hết cho 3 và 5, hãy in "FizzBuzz".

Đó là tất cả để. Tôi chắc chắn rằng đa số các bạn đã có thể hình dung hai hoặc ba câu lệnh nếu giải quyết điều này. Chúng ta hãy làm việc với điều này bằng cách sử dụng ngôn ngữ Bash Script.


Shebang

Shebang đề cập đến sự kết hợp của các ký tự dấu băm và dấu chấm than: #!. Loader của chương trình sẽ tìm kiếm một shebang trên dòng đầu tiên của script và sử dụng trình thông dịch được chỉ định trong nó. Một shebang bao gồm cú pháp sau: #!interpreter [parameters]. Trình thông dịch là chương trình được sử dụng để thông dịch ngôn ngữ của chúng tôi. Đối với bash script, đó sẽ là /bin /bash. Ví dụ: nếu bạn muốn tạo script trong PHP và chạy nó trong console, có lẽ bạn muốn sử dụng /usr/bin/php (hoặc đường dẫn đến file thực thi PHP trên máy của bạn) làm trình thông dịch.

Vâng, đường dẫn đó thực sự sẽ hiệu quả! Nó không đơn giản sao? Chỉ cần bảo đảm file của bạn thực thi đầu tiên. Khi bạn thực hiện, script này sẽ xuất thông tin PHP của bạn như bạn mong đợi.

Lời khuyên: Để đảm bảo rằng script của bạn sẽ hoạt động trên đa số các hê thống, bạn có thể sử dụng /bin/env trong shebang. Như vậy, thay vì /bin/bash, bạn có thể sử dụng /bin/env bash, nó sẽ hoạt động trên các hệ thống mà file bash thực thi không nằm trong /bin.


Văn bản kết quả

Như bạn mong đợi, kết quả của một script sẽ tương đương bất cứ gì được xuất ra từ câu lệnh của bạn. Tuy nhiên, nếu chúng ta muốn rõ ràng in một cái gì đó ra màn hình, chúng ta có thể sử dụng echo.

Chạy script này sẽ in "Hello World" trong console.


Các biến số

Như với bất kỳ ngôn ngữ lập trình nào, khi viết shell script, bạn có thể sử dụng các biến số.

Code này chính xác tạo ra cùng một thông điệp "Hello World". Như bạn có thể thấy, để gán giá trị cho một biến, chỉ cần viết tên của nó - loại trừ ký hiệu $ ở phía trước nó. Ngoài ra, hãy cẩn thận với các khoảng trắng; không thể có bất kỳ khoảng trắng nào giữa tên biến và dấu bằng. Vì vậy, ghi message="Hello" thay vì message='Hello'

Khi bạn muốn sử dụng một biến, bạn có thể lấy giá trị từ nó giống như chúng ta đã làm trong lệnh echo. Gắn $ phía trước tên của biến sẽ trả về giá trị của nó.

Lời khuyên: Dấu chấm phẩy không bắt buộc trong script bash. Bạn có thể sử dụng chúng trong đa số các trường hợp, nhưng hãy cẩn thận: có thể chúng có ý nghĩa khác với những gì bạn mong đợi.


In các số từ 1 đến 100

Tiếp tục với dự án demo, chúng tôi cần đi qua qua tất cả các số từ 1 đến 100. Để làm điều này, chúng tôi sẽ cần dùng vòng lặp for.

Có vài điều mới đáng chú ý trong ví dụ này - nhân tiện, in tất cả các số từ 1 đến 100, mỗi lần một số.

  • Cú pháp for trong Bash là: for VARIABLE in RANGE; do COMMAND done.
  • Các dấu ngoặc nhọn sẽ biến đổi 1..100 thành một range trong ví dụ của chúng tôi. Chúng cũng được sử dụng trong các bối cảnh khác, chúng tôi sẽ sớm xem xét.
  • dofor thực sự là hai câu lệnh riêng biệt. Nếu bạn muốn đặt hai lệnh trên một dòng, bạn sẽ cần tách biệt chúng bằng cách nào đó. Một cách là sử dụng dấu chấm phẩy. Ngoài ra, bạn có thể viết mã mà không có dấu chấm phẩy bằng cách do xuống dòng kế tiếp.

Quyết định đầu tiên

Bây giờ chúng tôi đã biết cách in tất cả các số từ 1 đến 100, đã đến lúc ra quyết định đầu tiên của chúng tôi.

Ví dụ này sẽ xuất "Fizz" cho các số chia hết cho 3. Một lần nữa, chúng ta phải xử lý một chút cú pháp mới. Hãy xử lý chúng từng số một.

  • if..then..else..fi - đây là cú pháp cổ điển cho một câu lệnh if trong Bash. Tất nhiên, else không bắt buộc - nhưng cần thiết cho logic của chúng tôi trong trường hợp này.
  • if COMMAND-RETURN-VALUE; then... - if sẽ thực thi nếu giá trị trả về của câu lệnh bằng 0. Đúng, logic trong Bash dựa trên cơ số 0, có nghĩa là các lệnh thực thi thoát thành công với code bằng 0. Nếu có lỗi, mặt khác, một số nguyên dương sẽ được trả về. Để đơn giản hóa mọi thứ: mọi kết quả khác 0 đều được coi là false.
  • Các biểu thức toán học trong Bash được chỉ định bởi dấu ngoặc đơn. $((number%3)) sẽ trả về giá trị còn lại của việc chia biến number cho 3. Xin lưu ý rằng chúng tôi không sử dụng $ bên trong dấu ngoặc đơn - mà ở phía trước chúng.

Bạn có thể tự hỏi vị trí của lệnh trong ví dụ của chúng tôi. Không phải chỉ có một dấu ngoặc có biểu thức kỳ lạ trong đó sao? À, nó ép [ thành một câu lệnh có thể thực thi. Để giải quyết vấn đề này, hãy thử các lệnh sau trong console của bạn.

Lời khuyên: Giá trị exit của lệnh luôn được trả về biến, ? (dấu chấm hỏi). Nó được ghi đè sau mỗi lần thực thi lệnh mới.


Đang kiểm tra Buzz

Chúng tôi đang làm tốt cho đến nay. Chúng tôi có "Fizz"; Bây giờ chúng ta hãy làm phần "Buzz".

Ở trên, chúng tôi đã đưa ra một điều kiện khác để chia hết cho 5: biểu thức elif. Điều này, tất nhiên, chuyển nó thành else if và sẽ được thực thi nếu lệnh theo sau nó trả về true (hoặc 0). Như bạn có thể quan sát, các câu lệnh có điều kiện nằm trong [] thường được đánh giá với sự trợ giúp của các tham số, chẳng hạn như -eq, viết tắt của "equals".

Đối với cú pháp, arg1 OP arg2, OP là một trong các -eq, -ne, -lt, -le, -gt hoặc -ge. Các toán tử nhị phân số học này trả về true nếu arg1 là bằng, không bằng, nhỏ hơn, nhỏ hơn hoặc bằng, lớn hơn, hoặc lớn hơn hoặc bằng arg2, tương ứng arg1arg2 có thể là số nguyên dương hoặc âm. - Bash Manual

Khi bạn đang cố gắng so sánh các chuỗi, bạn có thể sử dụng dấu == nổi tiếng hoặc thậm chí một dấu = duy nhất sẽ hiệu quả. != trả về true khi các chuỗi khác nhau.


Nhưng code này không hoàn toàn chính xác

Cho đến giờ code chạy, nhưng logic không chính xác. Khi số chia hết cho cả 3 và 5, logic của chúng tôi sẽ chỉ lặp lại "Fizz". Hãy sửa đổi Code của chúng tôi để đáp ứng yêu cầu cuối cùng của FizzBuzz.

Một lần nữa, chúng tôi đã phải thực hiện một số thay đổi. Điều đáng chú ý nhất là việc giới thiệu một biến, và sau đó ghép vào "Buzz" với nó, nếu cần thiết. Các string trong bash thường được xác định giữa dấu ngoặc kép ("). Dấu nháy đơn ' cũng có thể sử dụng được, nhưng để ghép nối dễ dàng hơn, nháy đôi " là lựa chọn tốt hơn. Trong các dấu nháy kép này, bạn có thể tham chiếu các biến: một số $variable text một số text khác, sẽ thay thế $variable với nội dung của nó. Khi bạn muốn ghép nối các biến với các chuỗi không có khoảng trắng giữa chúng, bạn có thể muốn đặt tên của biến trong dấu ngoặc nhọn. Trong đa số các trường hợp, như PHP, bạn không bắt buộc phải làm như vậy, nhưng điều đó rất hữu ích khi nói đến khả năng đọc của code.

Lời khuyên: Bạn không thể so sánh các chuỗi rỗng. Kết quả sẽ trả về một tham số bị thiếu.

Vì các đối số bên trong [ ] được coi là tham số, với "[", chúng phải khác với một chuỗi rỗng. Vì vậy, biểu thức này, mặc dù hợp lý, sẽ xuất ra lỗi: [$output !=""]. Đó là lý do tại sao chúng tôi đã sử dụng [-z $ output], trả về giá trị true nếu chuỗi có độ dài bằng 0.


Phương pháp trích xuất biểu thức logic

Một cách để cải thiện ví dụ của chúng tôi là trích xuất thành các hàm biểu thức toán học từ các câu lệnh if, như vậy:

Chúng tôi lấy các biểu thức so sánh phần còn lại với 0 và chuyển chúng thành một hàm. Thậm chí, chúng tôi đã loại bỏ sự so sánh với số 0, bởi vì số 0 có nghĩa là đúng với chúng tôi. Chúng ta chỉ phải trả về giá trị từ biểu thức toán học - rất đơn giản!

Lời khuyên: phần định nghĩa của hàm phải đặt trước lệnh gọi của nó.

Trong Bash, bạn có thể định nghĩa một phương thức là function func_name {commands; }. Tùy chọn, có một cú pháp thứ hai để khai báo các hàm: func_name () {commands; }. Vì vậy, chúng ta có thể bỏ chuỗi, function và thêm "()" vào sau tên của nó. Cá nhân tôi thích chọn lựa này, như được minh họa trong ví dụ trên. Nó rõ ràng hơn và giống với ngôn ngữ lập trình truyền thống.

Bạn không cần chỉ định các tham số cho hàm trong Bash. Gửi tham số cho một hảm được thực hiện bằng cách đơn giản là liệt kê chúng sau khi lệnh gọi hàm được phân tách bằng khoảng trắng. Không đặt dấu phẩy hoặc dấu ngoặc đơn trong lệnh gọi hàm - nó sẽ không hoạt động.

Các tham số nhận được được tự động gán cho các biến theo số. Tham số đầu tiên đi đến $1, tham số thứ hai đến $2, v.v. Biến đặc biệt, $0 đề cập đến tên của script hiện tại.

Hãy thử với các thông số

Code này sẽ xuất ra kết quả sau:

Hãy phân tích mã nguồn, từng dòng một.

  • Dòng cuối cùng là phần gọi hàm. Chúng tôi gọi nó với ba tham số string.
  • Dòng đầu tiên sau shebang là định nghĩa của hàm.
  • Dòng đầu tiên trong hàm xuất ra tham số đầu tiên: "one". Mọi việc cho đến nay thật đơn giản.
  • Dòng thứ hai xuất ra tên file của script hiện tại. Lần nữa rất đơn giản.
  • Dòng thứ ba thay đổi dấu phân cách ký tự mặc định thành chữ cái, "X". Theo mặc định, đây là " " (khoảng trắng). Đó là cách Bash nhận biết cách các tham số được phân tách.
  • Dòng thứ tư xuất ra một biến đặc biệt, $@. Nó đại diện cho tất cả các tham số bằng một từ duy nhất, chính xác như được chỉ định trong lệnh gọi hàm.
  • Dòng cuối cùng xuất ra một biến đặc biệt khác, $*. Nó đại diện cho tất cả các tham số, lấy từng thông số và nối với chữ cái đầu tiên của biến IFS. Đó là lý do kết quả là oneXtwoXthre.

Trả về chuỗi từ các hàm

Như tôi đã lưu ý trước đó, các hàm trong Bash chỉ có thể trả về các số nguyên. Như vậy, return "a string" sẽ là code không hợp lệ. Tuy nhiên, trong nhiều tình huống, bạn cần nhiều hơn kết quả một hoặc không. Chúng ta có thể cấu trúc lại ví dụ FizzBuzz của mình để trong câu lệnh for, chúng ta sẽ thực hiện một lệnh gọi hàm.

Vâng, đây là bước đầu tiên. Chúng tôi chỉ trích xuất tất cả code vào một hàm, được gọi là fizzOrBuzz và sau đó thay thế $number bằng $1. Tuy nhiên, tất cả kết quả xảy ra trong hàm fizzOrBuzz. Chúng tôi muốn xuất ra từ vòng lặp for bằng một câu lệnh echo, để chúng tôi có thể thêm trước mỗi dòng bằng một chuỗi khác. Chúng ta phải nắm bắt đầu ra của hàm fizzOrBuzz.

Chúng tôi đã cập nhật vòng lặp for một chút (không có thay đổi nào khác). Bây giờ chúng ta đã lặp lại mọi thứ hai lần theo hai cách khác nhau để minh họa sự khác biệt giữa hai giải pháp cho cùng một vấn đề.

Giải pháp đầu tiên để thu kết quả của một hàm hoặc một lệnh khác để sử dụng backticks. Trong 99% các trường hợp, giải pháp này sẽ hoạt động tốt. Bạn chỉ có thể tham chiếu một biến trong backticks bẳng tên của chúng, như chúng ta đã làm với $number. Một vài dòng đầu tiên của kết quả như sau:

Như bạn có thể thấy, mọi thứ đều được nhân đôi. Cho ra cùng kết quả.

Đối với giải pháp thứ hai, trước tiên chúng tôi đã chọn gán giá trị trả về cho một biến. Trong bài tập đó, chúng tôi đã sử dụng $(), trong trường hợp này, fork (phân nhánh) script, thực thi code và trả về kết quả của nó.


;, && and ||

Bạn có nhớ rằng chúng tôi đã sử dụng dấu chấm phẩy ở đây và ở đó? Chúng có thể được sử dụng để thực thi một số lệnh được viết trên cùng một dòng. Nếu bạn tách chúng bằng dấu chấm phẩy, chúng sẽ chỉ được thực thi.

Một trường hợp phức tạp hơn là sử dụng && giữa hai lệnh. Vâng, đó là một logic AND; điều đó có nghĩa là lệnh thứ hai sẽ chỉ được thực thi nếu lệnh thứ nhất trả về true (lệnh sẽ thoát khi giá trị là 0). Điều này rất hữu ích; chúng ta có thể đơn giản hóa các câu lệnh if thành các kiểu vắn tắt này:

Như hàm của chúng tôi, isDivisibleBy trả về giá trị return thích hợp, sau đó chúng tôi có thể sử dụng && để xét biến chúng tôi muốn. Những lệnh sau && sẽ chỉ được thực hiện nếu điều kiện là true. Theo cách tương tự, chúng ta có thể sử dụng || (ký tự ống đôi) dưới dạng OR. Dưới đây là một ví dụ nhanh:


Tổng kết

Vậy đó là tất cả cho hướng dẫn này! Tôi hy vọng rằng bạn đã thu được một số lời khuyên và kỹ thuật mới để viết các Bash script của riêng bạn. Cảm ơn đã đọc, và theo dõi các bài viết nâng cao hơn về chủ đề này.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.