如何在運行之前驗證 Linux Bash 腳本的語法
已發表: 2022-06-16Linux Bash 腳本中的錯誤和拼寫錯誤會在腳本運行時造成可怕的後果。 這裡有一些方法可以在你運行腳本之前檢查它們的語法。
那些討厭的蟲子
寫代碼很難。 或者更準確地說,編寫無錯誤的非平凡代碼很難。 並且程序或腳本中的代碼行越多,其中存在錯誤的可能性就越大。
您使用的編程語言對此有直接影響。 彙編編程比 C 編程困難得多,而 C 編程比 Python 編程更具挑戰性。 你編程的語言越低級,你自己做的工作就越多。 Python 可能喜歡內置的垃圾收集例程,但 C 和彙編肯定不喜歡。
編寫 Linux shell 腳本有其自身的挑戰。 使用像 C 這樣的編譯語言,稱為編譯器的程序會讀取您的源代碼(您在文本文件中鍵入的人類可讀指令)並將其轉換為二進制可執行文件。 二進製文件包含計算機可以理解和操作的機器代碼指令。
如果編譯器正在讀取和解析的源代碼符合語言的語法和其他規則,編譯器才會生成二進製文件。 如果您拼寫錯誤的保留字(語言的命令字之一)或變量名,編譯器將拋出錯誤。
例如,有些語言堅持在使用變量之前聲明它,而另一些語言則不那麼挑剔。 如果您使用的語言要求您聲明變量但您忘記這樣做,編譯器將拋出不同的錯誤消息。 儘管這些編譯時錯誤很煩人,但它們確實會發現很多問題並迫使您解決它們。 但是,即使您的程序沒有語法錯誤,也不意味著其中沒有錯誤。 離得很遠。
由於邏輯缺陷導致的錯誤通常更難發現。 如果您告訴您的程序添加二和三,但您真的希望它添加二和二,您將不會得到您期望的答案。 但是程序正在做它被編寫要做的事情。 程序的組成或語法沒有任何問題。 問題是你。 您編寫了一個格式良好的程序,但它並沒有按照您的意願行事。
測試很困難
徹底測試一個程序,即使是一個簡單的程序,也是非常耗時的。 運行幾次是不夠的; 您確實需要測試代碼中的所有執行路徑,以便驗證代碼的所有部分。 如果程序要求輸入,您需要提供足夠範圍的輸入值來測試所有條件——包括不可接受的輸入。
對於高級語言,單元測試和自動化測試有助於使徹底的測試成為可管理的練習。 所以問題是,有沒有什麼工具可以幫助我們編寫無錯誤的 Bash shell 腳本?
答案是肯定的,包括 Bash shell 本身。
使用 Bash 檢查腳本語法
Bash -n
(noexec) 選項告訴 Bash 讀取腳本並檢查它的語法錯誤,而不運行腳本。 根據您的腳本打算做什麼,這可能比運行它並尋找問題更安全。
這是我們要檢查的腳本。 它並不復雜,主要是一組if
語句。 它提示並接受一個代表一個月的數字。 腳本決定該月屬於哪個季節。 顯然,如果用戶根本沒有提供輸入,或者他們提供了無效輸入(如字母而不是數字),這將不起作用。
#! /bin/bash read -p "輸入月份(1到12):"月份 # 他們輸入了什麼嗎? 如果 [ -z "$month" ] 然後 echo "您必須輸入一個代表月份的數字。" 1號出口 菲 # 月份有效嗎? if (( "$month" < 1 || "$month" > 12)); 然後 echo "月份必須是 1 到 12 之間的數字。" 出口 0 菲 #現在是春節嗎? if (( "$month" >= 3 && "$month" < 6)); 然後 迴聲“那是一個春天的月份。” 出口 0 菲 #現在是暑假嗎? if (( "$month" >= 6 && "$month" < 9)); 然後 迴聲“那是一個夏天的月份。” 出口 0 菲 #現在是秋天嗎? if (( "$month" >= 9 && "$month" < 12)); 然後 echo "那是秋天的月份。" 出口 0 菲 # 一定是冬月 迴聲“那是一個冬天的月份。” 出口 0
此部分檢查用戶是否輸入了任何內容。 它測試$month
變量是否未設置。
如果 [ -z "$month" ] 然後 echo "您必須輸入一個代表月份的數字。" 1號出口 菲
此部分檢查他們是否輸入了 1 到 12 之間的數字。它還捕獲不是數字的無效輸入,因為字母和標點符號不會轉換為數值。
# 月份有效嗎? if (( "$month" < 1 || "$month" > 12)); 然後 echo "月份必須是 1 到 12 之間的數字。" 出口 0 菲
所有其他 If 子句檢查$month
變量中的值是否介於兩個值之間。 如果是,則該月屬於該季節。 例如,如果用戶輸入的月份是 6、7 或 8,則它是夏季月份。
#現在是暑假嗎? if (( "$month" >= 6 && "$month" < 9)); 然後 迴聲“那是一個夏天的月份。” 出口 0 菲
如果您想完成我們的示例,請將腳本文本複制並粘貼到編輯器中,並將其保存為“seasons.sh”。 然後使用chmod
命令使腳本可執行:
chmod +x seasons.sh
我們可以通過以下方式測試腳本
- 根本不提供任何輸入。
- 提供非數字輸入。
- 提供 1 到 12 範圍之外的數值。
- 提供 1 到 12 範圍內的數值。
在所有情況下,我們都使用相同的命令啟動腳本。 唯一的區別是用戶在腳本提升時提供的輸入。
./seasons.sh
這似乎按預期工作。 讓 Bash 檢查我們腳本的語法。 我們通過調用-n
(noexec) 選項並傳入我們的腳本名稱來做到這一點。
bash -n ./seasons.sh
這是一個“沒有消息就是好消息”的案例。 默默地讓我們回到命令提示符是 Bash 表示一切似乎都很好的方式。 讓我們破壞我們的腳本並引入一個錯誤。
我們將從第一個if
子句中刪除then
。
# 月份有效嗎? if (( "$month" < 1 || "$month" > 12)); # "then" 已被刪除 echo "月份必須是 1 到 12 之間的數字。" 出口 0 菲
現在讓我們運行腳本,首先沒有用戶輸入,然後有用戶輸入。
./seasons.sh
第一次運行腳本時,用戶沒有輸入值,因此腳本終止。 我們破壞的部分永遠不會到達。 腳本結束時沒有來自 Bash 的錯誤消息。
第二次運行腳本時,用戶提供一個輸入值,並執行第一個 if 子句以檢查用戶輸入的完整性。 這會觸發來自 Bash 的錯誤消息。
請注意,Bash 檢查該子句的語法——以及所有其他代碼行——因為它不關心腳本的邏輯。 當 Bash 檢查腳本時,不會提示用戶輸入數字,因為腳本沒有運行。
腳本的不同可能執行路徑不會影響 Bash 檢查語法的方式。 Bash 簡單而有條不紊地從腳本頂部到底部運行,檢查每一行的語法。
ShellCheck 實用程序
linter(以 Unix 鼎盛時期的 C 源代碼檢查工具命名)是一種代碼分析工具,用於檢測編程錯誤、風格錯誤以及對該語言的可疑或可疑使用。 Linter 可用於許多編程語言,並以迂腐著稱。 並非 linter 發現的所有內容本身都是錯誤,但它們引起您注意的任何內容都可能值得關注。
ShellCheck 是一個用於 shell 腳本的代碼分析工具。 它的行為就像 Bash 的 linter。
讓我們將丟失的保留字放回我們的腳本中, then
嘗試其他方法。 我們將從第一個if
子句中刪除左括號“[”。
# 他們輸入了什麼嗎? if -z "$month" ] # 左括號 "[" 被移除 然後 echo "您必須輸入一個代表月份的數字。" 1號出口 菲
如果我們使用 Bash 檢查腳本,它不會發現問題。
bash -n seasons.sh
./seasons.sh
但是當我們嘗試運行腳本時,我們會看到一條錯誤消息。 而且,儘管出現錯誤消息,腳本仍會繼續執行。 這就是為什麼一些錯誤如此危險的原因。 如果在腳本中進一步採取的操作依賴於用戶的有效輸入,則腳本的行為將是不可預測的。 它可能會使數據面臨風險。
Bash -n
(noexec) 選項在腳本中找不到錯誤的原因是左括號“[”是一個名為[
的外部程序。 它不是 Bash 的一部分。 這是使用test
命令的一種簡寫方式。
Bash 在驗證腳本時不會檢查外部程序的使用。
安裝 ShellCheck
ShellCheck 需要安裝。 要在 Ubuntu 上安裝它,請鍵入:
sudo apt install shellcheck
要在 Fedora 上安裝 ShellCheck,請使用此命令。 請注意,包名稱是混合大小寫的,但是當您在終端窗口中發出命令時,它都是小寫的。
須藤 dnf 安裝 ShellCheck
在 Manjaro 和類似的基於 Arch 的發行版上,我們使用pacman
:
sudo pacman -S shellcheck
使用 ShellCheck
讓我們嘗試在我們的腳本上運行 ShellCheck。
shellcheck seasons.sh
ShellCheck 發現問題並將其報告給我們,並提供一組鏈接以獲取更多信息。 如果您右鍵單擊一個鏈接並從出現的上下文菜單中選擇“打開鏈接”,該鏈接將在您的瀏覽器中打開。
ShellCheck 還發現了另一個不那麼嚴重的問題。 它以綠色文本報告。 這表明這是一個警告,而不是徹底的錯誤。
讓我們糾正我們的錯誤並替換缺少的“[。” 一種錯誤修復策略是首先糾正最高優先級的問題,然後再處理較低優先級的問題,例如稍後的警告。
我們替換了缺失的“[”並再次運行 ShellCheck。
shellcheck seasons.sh
ShellCheck 的唯一輸出是指我們之前的警告,所以這很好。 我們沒有需要修復的高優先級問題。
警告告訴我們,使用沒有-r
(按原樣讀取)選項的read
命令將導致輸入中的任何反斜杠被視為轉義字符。 這是 linter 可以生成的迂腐輸出類型的一個很好的例子。 在我們的例子中,用戶無論如何都不應該輸入反斜杠——我們需要他們輸入一個數字。
像這樣的警告需要程序員進行判斷。 努力修復它,還是讓它保持原樣? 這是一個簡單的兩秒鐘修復。 它會阻止警告使 ShellCheck 的輸出變得混亂,所以我們不妨接受它的建議。 我們將添加一個“r”來選擇read
命令上的標誌,並保存腳本。
read -pr "輸入月份(1 到 12):" 月份
再次運行 ShellCheck 為我們提供了一份乾淨的健康賬單。
ShellCheck 是您的朋友
ShellCheck 可以檢測、報告和建議各種問題。 查看他們的錯誤代碼庫,其中顯示了它可以檢測到多少類型的問題。
它是免費的、快速的,並且減輕了編寫 shell 腳本的痛苦。 有什麼不喜歡的?