Lỗi và lỗi chính tả trong tập lệnh Linux Bash có thể gây ra những điều tồi tệ khi tập lệnh được chạy. Dưới đây là một số cách để kiểm tra cú pháp của tập lệnh trước khi bạn chạy chúng.
Những con bọ phiền phức đó
Viết mã là khó. Hay nói chính xác hơn, viết mã không tầm thường không có lỗi là khó. Và càng có nhiều dòng mã trong một chương trình hoặc tập lệnh, thì càng có nhiều khả năng sẽ có lỗi trong đó.
Ngôn ngữ bạn lập trình có ảnh hưởng trực tiếp đến điều này. Lập trình hợp ngữ khó hơn nhiều so với lập trình bằng C và lập trình bằng C khó hơn lập trình bằng Python. Ngôn ngữ bạn đang lập trình càng cấp thấp, bạn càng phải tự làm nhiều việc hơn. Python có thể tận hưởng các thói quen thu gom rác được xây dựng sẵn, nhưng C và hợp ngữ thì không.
Viết các tập lệnh shell Linux đặt ra những thách thức riêng. Với một ngôn ngữ được biên dịch như C, một chương trình được gọi là trình biên dịch sẽ đọc mã nguồn của bạn—các hướng dẫn mà con người có thể đọc được mà bạn nhập vào một tệp văn bản—và biến nó thành một tệp thực thi nhị phân. Tệp nhị phân chứa các hướng dẫn mã máy mà máy tính có thể hiểu và thực hiện.
Trình biên dịch sẽ chỉ tạo tệp nhị phân nếu mã nguồn mà nó đang đọc và phân tích cú pháp tuân theo cú pháp và các quy tắc khác của ngôn ngữ. Nếu bạn đánh vần một từ dành riêng—một trong những từ lệnh của ngôn ngữ—hoặc một tên biến không chính xác, trình biên dịch sẽ báo lỗi.
Ví dụ, một số ngôn ngữ yêu cầu bạn khai báo một biến trước khi sử dụng nó, một số ngôn ngữ khác thì không quá cầu kỳ. Nếu ngôn ngữ bạn đang làm việc yêu cầu bạn khai báo các biến nhưng bạn quên khai báo, trình biên dịch sẽ đưa ra một thông báo lỗi khác. Dù khó chịu như những lỗi thời gian biên dịch này, nhưng chúng gây ra rất nhiều vấn đề và buộc bạn phải giải quyết chúng. Nhưng ngay cả khi bạn có một chương trình không có lỗi cú pháp nó không có nghĩa là không có lỗi trong đó. Cách xa nó.
Lỗi do lỗi logic thường khó phát hiện hơn nhiều. Nếu bạn yêu cầu chương trình cộng hai và ba nhưng bạn thực sự muốn nó cộng hai và hai, thì bạn sẽ không nhận được câu trả lời như mong đợi. Nhưng chương trình đang làm những gì nó đã được viết để làm. Không có gì sai với thành phần hoặc cú pháp của chương trình. Vấn đề là bạn. Bạn đã viết một chương trình được thiết kế tốt nhưng không thực hiện được những gì bạn muốn.
Thử nghiệm là khó khăn
Kiểm tra kỹ lưỡng một chương trình, thậm chí là một chương trình đơn giản, rất tốn thời gian. Chạy nó một vài lần là không đủ; bạn thực sự cần kiểm tra tất cả các đường dẫn thực thi trong mã của mình để tất cả các phần của mã được xác minh. Nếu chương trình yêu cầu đầu vào, bạn cần cung cấp đủ phạm vi giá trị đầu vào để kiểm tra tất cả các điều kiện—kể cả đầu vào không được chấp nhận.
Đối với các ngôn ngữ cấp cao hơn, các bài kiểm tra đơn vị và kiểm tra tự động giúp kiểm tra kỹ lưỡng thành một bài tập có thể quản lý được. Vì vậy, câu hỏi đặt ra là, có bất kỳ công cụ nào mà chúng tôi có thể sử dụng để giúp chúng tôi viết các tập lệnh trình bao Bash không có lỗi không?
Câu trả lời là có, bao gồm cả Bash shell.
Sử dụng Bash để kiểm tra cú pháp tập lệnh
Cú đánh -n
(noexec) yêu cầu Bash đọc tập lệnh và kiểm tra lỗi cú pháp mà không cần chạy tập lệnh. Tùy thuộc vào kịch bản của bạn dự định làm gì, điều này có thể an toàn hơn nhiều so với việc chạy nó và tìm kiếm sự cố.
Đây là kịch bản chúng tôi sẽ kiểm tra. Nó không phức tạp, nó chủ yếu là một bộ if
các câu lệnh. Nó nhắc và chấp nhận một số đại diện cho một tháng. Kịch bản quyết định tháng thuộc về mùa nào. Rõ ràng, điều này sẽ không hoạt động nếu người dùng hoàn toàn không cung cấp đầu vào hoặc nếu họ cung cấp đầu vào không hợp lệ như một chữ cái thay vì một chữ số.
#! /bin/bash read -p "Enter a month (1 to 12): " month # did they enter anything? if [ -z "$month" ] then echo "You must enter a number representing a month." exit 1 fi # is it a valid month? if (( "$month" < 1 || "$month" > 12)); then echo "The month must be a number between 1 and 12." exit 0 fi # is it a Spring month? if (( "$month" >= 3 && "$month" < 6)); then echo "That's a Spring month." exit 0 fi # is it a Summer month? if (( "$month" >= 6 && "$month" < 9)); then echo "That's a Summer month." exit 0 fi # is it an Autumn month? if (( "$month" >= 9 && "$month" < 12)); then echo "That's an Autumn month." exit 0 fi # it must be a Winter month echo "That's a Winter month." exit 0
Phần này kiểm tra xem người dùng đã nhập gì chưa. Nó kiểm tra xem liệu $month
biến không được đặt.
if [ -z "$month" ] then echo "You must enter a number representing a month." exit 1 fi
Phần này kiểm tra xem họ có nhập số từ 1 đến 12 hay không. Phần này cũng chặn đầu vào không hợp lệ không phải là chữ số, vì các chữ cái và ký hiệu dấu chấm câu không chuyển thành giá trị số.
# is it a valid month? if (( "$month" < 1 || "$month" > 12)); then echo "The month must be a number between 1 and 12." exit 0 fi
Tất cả các mệnh đề If khác kiểm tra xem giá trị trong $month
biến nằm giữa hai giá trị. Nếu có thì tháng đó thuộc mùa đó. Ví dụ: nếu tháng do người dùng nhập là 6, 7 hoặc 8 thì đó là tháng Mùa hè.
# is it a Summer month? if (( "$month" >= 6 && "$month" < 9)); then echo "That's a Summer month." exit 0 fi
Nếu bạn muốn xem qua các ví dụ của chúng tôi, hãy sao chép và dán văn bản của tập lệnh vào trình chỉnh sửa và lưu dưới dạng “ Seasons.sh”. Sau đó làm cho tập lệnh có thể thực thi được bằng cách sử dụng chmod
yêu cầu:
chmod +x seasons.sh
Chúng ta có thể kiểm tra kịch bản bằng cách
- Không cung cấp đầu vào nào cả.
- Cung cấp đầu vào không phải là số.
- Cung cấp một giá trị số nằm ngoài phạm vi từ 1 đến 12.
- Cung cấp các giá trị số trong phạm vi từ 1 đến 12.
Trong mọi trường hợp, chúng tôi bắt đầu tập lệnh bằng cùng một lệnh. Sự khác biệt duy nhất là đầu vào mà người dùng cung cấp khi được quảng cáo bởi tập lệnh.
./seasons.sh
Điều đó dường như làm việc như mong đợi. Hãy để Bash kiểm tra cú pháp của tập lệnh của chúng ta. Chúng tôi làm điều này bằng cách gọi các -n
(noexec) và chuyển tên tập lệnh của chúng tôi.
bash -n ./seasons.sh
Đây là một trường hợp “không có tin tức là tin tốt.” Âm thầm đưa chúng tôi trở lại dấu nhắc lệnh là cách Bash nói rằng mọi thứ có vẻ ổn. Hãy phá hoại kịch bản của chúng tôi và giới thiệu một lỗi.
Chúng tôi sẽ loại bỏ then
từ đầu tiên if
khoản.
# is it a valid month? if (( "$month" < 1 || "$month" > 12)); # "then" has been removed echo "The month must be a number between 1 and 12." exit 0 fi
Bây giờ, hãy chạy tập lệnh, đầu tiên không có và sau đó có đầu vào từ người dùng.
./seasons.sh
Lần đầu tiên tập lệnh được chạy, người dùng không nhập giá trị và do đó, tập lệnh sẽ kết thúc. Phần mà chúng tôi đã phá hoại không bao giờ đạt được. Tập lệnh kết thúc mà không có thông báo lỗi từ Bash.
Lần thứ hai tập lệnh được chạy, người dùng cung cấp một giá trị đầu vào và mệnh đề if đầu tiên được thực thi để kiểm tra đầu vào của người dùng. Điều đó kích hoạt thông báo lỗi từ Bash.
Lưu ý rằng Bash kiểm tra cú pháp của mệnh đề đó—và mọi dòng mã khác—bởi vì nó không quan tâm đến Hợp lý của kịch bản. Người dùng không được nhắc nhập số khi Bash kiểm tra tập lệnh vì tập lệnh không chạy.
Các đường dẫn thực thi khác nhau có thể có của tập lệnh không ảnh hưởng đến cách Bash kiểm tra cú pháp. Bash hoạt động đơn giản và có phương pháp từ đầu đến cuối tập lệnh, kiểm tra cú pháp cho mọi dòng.
Tiện ích ShellCheck
Kẻ nói dối—được đặt tên theo công cụ kiểm tra mã nguồn C từ thời hoàng kim của Unix—là một công cụ phân tích mã được sử dụng để phát hiện lỗi lập trình, lỗi văn phong và cách sử dụng ngôn ngữ đáng ngờ hoặc có vấn đề. Linters có sẵn cho nhiều ngôn ngữ lập trình và nổi tiếng là mô phạm. Không phải mọi thứ kẻ nói dối tìm thấy đều là lỗi mỗi gia nhậpnhưng bất cứ điều gì họ làm cho bạn chú ý đều có thể đáng được chú ý.
ShellCheck là một công cụ phân tích mã cho shell script. Nó hoạt động như một kẻ nói dối cho Bash.
Hãy đặt thiếu của chúng tôi then
từ dành riêng trở lại tập lệnh của chúng tôi và thử một cái gì đó khác. Chúng tôi sẽ xóa dấu ngoặc mở “[”ngaytừđầu[”fromtheveryfirstif
khoản.
# did they enter anything? if -z "$month" ] # opening bracket "[" removed then echo "You must enter a number representing a month." exit 1 fi
nếu chúng tôi sử dụng Bash để kiểm tra tập lệnh thì nó không phát hiện ra vấn đề gì.
bash -n seasons.sh
./seasons.sh
Nhưng khi chúng ta cố gắng chạy tập lệnh, chúng tôi thấy một thông báo lỗi. Và, bất chấp thông báo lỗi, tập lệnh vẫn tiếp tục thực thi. Đây là lý do tại sao một số lỗi rất nguy hiểm. Nếu các hành động được thực hiện tiếp theo trong tập lệnh dựa trên đầu vào hợp lệ từ người dùng, hành vi của tập lệnh sẽ không thể đoán trước. Nó có khả năng gây rủi ro cho dữ liệu.
Lý do Bash -n
(noexec) tùy chọn không tìm thấy lỗi trong tập lệnh là dấu ngoặc mở “[”làmộtchươngtrìnhbênngoàicótên[”isanexternalprogramcalled[
. Nó không phải là một phần của Bash. Đó là một cách viết tắt của việc sử dụng test
yêu cầu.
Bash không kiểm tra việc sử dụng các chương trình bên ngoài khi nó đang xác thực tập lệnh.
Cài đặt ShellCheck
ShellCheck yêu cầu cài đặt. Để cài đặt nó trên Ubuntu, gõ:
sudo apt install shellcheck
Để cài đặt ShellCheck trên Fedora, hãy sử dụng lệnh này. Lưu ý rằng tên gói ở dạng hỗn hợp, nhưng khi bạn đưa ra lệnh trong cửa sổ đầu cuối thì tất cả đều ở dạng chữ thường.
sudo dnf install ShellCheck
Trên Manjaro và các bản phân phối dựa trên Arch tương tự, chúng tôi sử dụng pacman
:
sudo pacman -S shellcheck
Sử dụng ShellCheck
Hãy thử chạy ShellCheck trên tập lệnh của chúng ta.
shellcheck seasons.sh
ShellCheck tìm ra vấn đề và báo cáo cho chúng tôi, đồng thời cung cấp một tập hợp các liên kết để biết thêm thông tin. Nếu bạn nhấp chuột phải vào một liên kết và chọn “Mở liên kết” từ trình đơn ngữ cảnh xuất hiện, liên kết đó sẽ mở trong trình duyệt của bạn.
ShellCheck cũng tìm thấy một vấn đề khác, vấn đề này không nghiêm trọng bằng. Nó được báo cáo trong văn bản màu xanh lá cây. Điều này cho thấy đó là một cảnh báo, không phải lỗi hoàn toàn.
Hãy sửa lỗi của chúng ta và thay thế “[”Mộtchiếnlượcsửalỗilàsửacácvấnđềcómứcđộưutiêncaonhấttrướcvàxửlýcácvấnđềcómứcđộưutiênthấphơnnhưcảnhbáosau[”Onebug-fixstrategyistocorrectthehighestpriorityissuesfirstandworkdowntothelowerpriorityissueslikewarningslater
Chúng tôi đã thay thế “[”bịthiếuvàchạyShellCheckmộtlầnnữa[”andranShellCheckoncemore
shellcheck seasons.sh
Đầu ra duy nhất từ ShellCheck đề cập đến cảnh báo trước đây của chúng tôi, vì vậy điều đó tốt. Chúng tôi không có vấn đề ưu tiên cao cần sửa chữa.
Cảnh báo cho chúng ta biết rằng sử dụng read
lệnh mà không có -r
(read as-is) sẽ khiến mọi dấu gạch chéo ngược trong đầu vào được coi là ký tự thoát. Đây là một ví dụ điển hình về loại đầu ra mô phạm mà một kẻ nói dối có thể tạo ra. Trong trường hợp của chúng tôi, người dùng không nên nhập dấu gạch chéo ngược—chúng tôi cần họ nhập số.
Những cảnh báo như thế này yêu cầu lập trình viên phải có phán quyết. Hãy nỗ lực để sửa chữa nó, hoặc để nó như vậy? Đó là một sửa chữa hai giây đơn giản. Và nó sẽ ngăn cảnh báo làm lộn xộn đầu ra của ShellCheck, vì vậy chúng tôi cũng có thể làm theo lời khuyên của nó. Chúng tôi sẽ thêm một “r” để tùy chọn các cờ trên read
lệnh và lưu tập lệnh.
read -pr "Enter a month (1 to 12): " month
Chạy ShellCheck một lần nữa mang lại cho chúng tôi một hóa đơn sạch sẽ.
ShellCheck là bạn của bạn
ShellCheck có thể phát hiện, báo cáo và tư vấn về một loạt vấn đề. Kiểm tra bộ sưu tập mã xấu của họ, cho biết có bao nhiêu loại vấn đề mà nó có thể phát hiện.
Nó miễn phí, nhanh chóng và không tốn nhiều công sức khi viết shell script. Có gì không thích?