最好的 Bash 指令碼不僅能正常工作,而且編寫得易於理解和修改。這得益於採用一致的變數名和編碼風格。驗證使用者提供引數的合法性並檢查命令是否成功執行也能保證指令碼長時間可用。下面是一些我個人行之有效的建議。
採用一致縮排
縮排使程式碼更具可讀性,也因此更具可維護性,尤其在程式碼邏輯巢狀超過三層。縮排使得指令碼邏輯的基本結構非常直觀。至於縮排多少空格無關緊要,儘管大部分人都傾向於使用4個或8個空格。只要確保採用縮排並進行對齊,這樣就好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#!/bin/bash if [ $# -ge 1 ] && [ -d $1 ]; then for file in `ls $1` do if [ $debug == "on" ]; then echo working on $file fi wc -l $1/$file done else echo "USAGE: $0 directory" exit 1 fi |
提供有效資訊
有效資訊可以幫助執行指令碼的人瞭解他們需要提供哪些引數,甚至是對兩年後你自己。
1 2 3 4 |
if [ $# == 0 ]; then echo "Usage: $0 filename" exit 1 fi |
合理使用註釋
提供註釋可以解釋你的程式碼,特別是當程式碼比較複雜時,但不需要解釋顯而易見的程式碼行,只需要解釋使用的每一條命令或者在程式碼段容易弄混的重要程式碼行。
1 2 3 4 5 6 7 8 |
username=$1 # make sure the account exists on the system grep ^$username: /etc/passwd if [ $? != 0 ]; then echo "No such user: $username" exit 1 fi |
在出錯退出時返回錯誤碼
即使你不會檢視錯誤碼,但在程式碼出錯時返回非零值是個不錯的主意。有一天,也許你想找一種簡單的方法來檢查指令碼哪裡出錯,那麼返回值1或4或11可以幫你很快弄明白。
1 2 3 4 5 6 7 |
echo -n "In what year were you born?> " read year if [ $year -gt `date +%Y` ]; then echo "Sorry, but that's just not possible." exit 2 fi |
使用函式替換重複命令集
函式也能讓你的程式碼更具可讀性和可維護性。如果重複使用的命令只有一條就不必麻煩,但如果很容易分離出一小撮共用命令列,就很有必要這樣做。如果以後需要進行改動,只需要在一處進行即可。
1 2 3 4 5 6 7 |
function lower() { local str="$@" local output output=$(tr '[A-Z]' '[a-z]'<<<"${str}") echo $output } |
為變數取有實際意義的名稱
Unix管理員通常儘量避免輸入一些額外字元,但不要在指令碼中這樣做。花些額外時間給變數一個有意義的命名並注意命名的一致性。
1 2 3 4 5 6 7 8 |
#!/bin/bash if [ $# != 1 ]; then echo "Usage: $0 address" exit 1 else ip=$1 fi |
檢查引數型別是否正確
如果在使用引數前,對提供給指令碼的輸入引數進行型別檢查,可以避免很多麻煩。下面是一種簡單的方法,用於檢查引數是否是數字。
1 2 3 4 5 |
if ! [ "$1" -eq "$1" 2> /dev/null ] then echo "ERROR: $1 is not a number!" exit 1 fi |
檢查引數缺失或提供引數順序的錯誤資訊
不要以為使用者知道自己在做什麼。如果他應該提供多個引數,請確保他提供了正確的引數。
1 2 3 |
if [ $# != 3 ]; then echo "What part of THREE ARGUMENTS don't you understand?" fi |
檢查必要檔案是否真實存在
在使用一個檔案前檢查它是否存在非常簡單。下面的簡單例子用於檢查第一個輸入引數指定的檔案在系統上是否真實存在。
1 2 3 |
if [ ! -f $1 ]; then echo "$1 -- no such file" fi |
輸出傳送至/dev/null
將命令輸出傳送到 /dev/null 並以一種更加友好的方式告訴使用者哪裡出錯,可以讓你的指令碼對使用者而言更加簡單。
1 2 3 4 5 6 7 8 9 10 11 12 |
if [ $1 == "help" ]; then echo "Sorry -- No help available for $0" else CMD=`which $1 >/dev/null 2>&1` if [ $? != 0 ]; then echo "$1: No such command -- maybe misspelled or not on your search path" exit 2 else cmd=`basename $1` whatis $cmd fi fi |
使用錯誤碼
你可以在指令碼中使用返回碼來判定一條命令的執行結果是否符合預期。
1 2 3 4 5 6 |
# check if the person is still logged in or has running processes ps -U $username 2> /dev/null if [ $? == 0 ]; then echo "processes:" >> /home/oldaccts/$username ps -U $username >> /home/oldaccts/$username fi |
資訊提示
不要忘記告訴執行指令碼的人他們應該知道的內容。他們不必在閱讀程式碼後才知道你為他們建立了一個檔案,特別是建立的檔案不在當前資料夾中。
1 2 3 |
... date >> /tmp/report$$ echo "Your report is /tmp/report$$" |
引用所有引數擴充套件
如果在指令碼中使用字元擴充套件,別忘了使用引號,這樣就不會得到一個意料之外的結果。
1 2 3 4 5 6 7 |
#!/bin/bash msg="Be careful to name your files *.txt" # this will expand *.txt echo $msg # this will not echo "$msg" |
引用所有引數時使用$@
$@變數會列出所有提供給指令碼的引數,並且非常容易使用,正如下面一段指令碼摘錄所示。
1 2 3 4 5 6 |
#!/bin/bash for i in "$@" do echo "$i" done |
一些額外的注意和一致性可能意味著你現在編寫的指令碼將在之後很多年都易於使用。