使用bash編寫Linux shell指令碼–複合命令

來源:互聯網
上載者:User

來源:http://blog.csdn.net/fox_lht/article/details/5897336

除了最簡單的指令碼,你很少想要執行每一個命令。執行一組命令或者重複執行一組命令若干次比執行單個命令更加有助。複合命令是將命令封裝在一組其他命令中。

從可讀性來說,封裝後的命令使用縮排格式將會使複合命令的代碼清晰並便於閱讀。管理員曾經抱怨過我的縮排比標準的縮排少了一個空格(我必須使用尺子在螢幕上測量才能確定此事),我認為這不是什麼問題,但是他說,當輸入 0 時,它的程式會崩潰。

複合命令總是有兩個命令組成。命令的結束符是該命令相反拼字順序,就像使用括弧將命令括住了。例如:神秘莫測的命令 esac 實際上是複合命令 case 的結束符。

命令狀態代碼

每一個 Linux 命令都返回一個狀態代碼(退出狀態),他是一個 0~255 之間的數字,用來表示該命令遇到的問題。如果狀態代碼返回的是 0 ,則表示該命令運行成功,其他的狀態代碼表示某種錯誤。

狀態代碼包含在變數“ $? ”中。

$ unzip no_file.zip

unzip:  cannot find no_file.zip, no_file.zip.zip or no_file.zip.ZIP.

$ printf “%d/n” “$?”

9

unzip 命令找不到要解壓的檔案,返回的狀態代碼是 9 。

非官方的 Linux 慣例使用狀態代碼 127 並且比標準的錯誤碼要小。例如: ls 返回了狀態代碼 9 ,它表示“ bad file number ”。完整的錯誤碼列在附錄 D :“錯誤碼”中。

如果命令被訊號中斷, Bash 返回狀態代碼 128 ,加上訊號碼。最終,使用者的錯誤碼應該大於 191 , Bash 返回的錯誤碼為 63 。訊號碼列在附錄 E :訊號。

if test ! -x “$who” ; then

printf “$SCRIPT:$LINENO: the command $who is not available – “/

“ aborting/n “ >&2

exit 192

fi

一般,大部分 Linux 命令只是簡單的返回 1 或 0 ,表示失敗還是成功。這也許就是你的指令碼所需要的所有資訊。特殊的錯誤資訊任然顯示在標準輸出上。

$ ls po_1473.txt

po_1473.txt

$ printf “%d/n” $?

0

$ ls no_file

no_file not found

$ printf “%d/n” $?

1

狀態代碼不同於 let 命令返回的真值(第六章討論過),本節稱之為邏輯運算式。在 let 命令中, false 的值是 0 ,這符合電腦語言的習慣,但是狀態代碼是 0 表示成功而不是失敗。

$ let “RESULT=1>0”

$ printf “%d %d/n” “$RESULT” $?

1 0

$ test 1 -gt 0

$ printf “%d/n” $?

0

let 命令分配 1 給 RESULT ,表明 1 大於 0 。 test 命令返回狀態代碼 0 表明命令運行成功。 let 命令返回狀態代碼 0 ,表明 let 命令成功進行比較。

這些相反的碼和習慣可能會導致錯誤,這些錯誤很難調試出來。 Bash 有兩個內建命令 true 和 false 。這些是返回的狀態代碼,而不是 let 命令的真值。

$ true

$ printf “%d/n” “$?”

0

$ false

$ printf “%d/n” “$?”

1

true 命令分配一個成功的狀態代碼( 0 )。 fasle 分配一個錯誤的狀態代碼( 1 )。

有點混亂吧?

如果你需要儲存邏輯比較的成功狀態最好還是使用 test 命令。大部分外殼使用狀態代碼而不是真值。

在管道中,一次運行幾個命令。從管道返回的狀態代碼是最後一個命令的狀態代碼。下面的樣本中,顯示的是 wc 命令而不是 ls 命令的狀態代碼。

$ ls badfile.txt | wc -l

ls: badfile.txt: No such file or directory

0

$ printf “%d/n” “$?”

0

雖然 ls 報告了一個錯誤,管道返回的還是成功的狀態代碼,因為 wc 命令是運行成功的。

Bash 也定義了一個數組稱之為 PIPESTATUS ,它包含了上此運行管道中每一個命令的單獨狀態。

$ ls badfile.txt | wc -l

ls: badfile.txt: No such file or directory

0

$ printf “%d %d/n” “${PIPESTATUS[0]}” “${PIPESTATUS[1]}”

1 0

$? 是 PIPESTATUS 數組的最後一個值的別名。

一個命令或管道可以被“!”進行對狀態進行取反操作,如果狀態時 0 取反則為 1 , 如果大於 0 ,取反則為 0 。

if 命令

if 命令執行二選一或多選一的操作。

通常 if 命令和 test 命令一起使用。

NUM_ORDERS=`ls -1 | wc -l`

if [ “$NUM_ORDERS” -lt “$CUTOFF” ] ; then

printf “%s/n” “Too few orders...try running again later”

exit 192

fi

這個例子是對目前的目錄中的檔案進行統計,如果沒有足夠的檔案數,則顯示一則訊息,否則就到 fi 命令結束。

then 命令前分號是必須要有的,雖然它是和 if 一起工作的,但是它仍然是一個單獨的命令,所以需要分號進行分割。

if 命令亦可以有一個 else 命令的分支,它可以在條件失敗的時候運行。

NUM_ORDERS=`ls -1 | wc -l`

if [ “$NUM_ORDERS” -lt “$CUTOFF” ] ; then

printf “%s/n” “Too few orders...but will process them anyway”

else

printf “%s/n” “Starting to process the orders”

fi

if 命令內部可以嵌套 if 命令。

NUM_ORDERS=`ls -1 | wc -l`

if [[ $NUM_ORDERS -lt $TOOFEW ]] ; then

printf “%s/n” “Too few orders...but will process them anyway”

else

if [[ $NUM_ORDERS -gt $TOOMANY ]] ; then

printf “%s/n” “There are many orders.  Processing may take a long time”

else

printf “%s/n” “Starting to process the orders”

fi

fi

if 不可以交叉嵌套,即:裡面的 if 必須完全在外部 if 命令內。

為了實現多分支, if 命令可以有 elif 分支, elif 命令是 else if 的簡寫,它可以減少不必要的嵌套。 elif 命令的最後可以在最後加一個 else 命令,他在所有條件都沒有中的時候執行。有了這些知識,你可以重寫上面的樣本:

NUM_ORDERS=`ls -1 | wc -l`

if [ “$NUM_ORDERS” -lt “$TOOFEW” ] ; then

printf “%s/n” “Too few orders...but will process them anyway”

elif [ “$NUM_ORDERS” -gt “$TOOMANY” ] ; then

printf “%s/n” “There are many orders.  Processing may take a long time”

else

printf “%s/n” “Starting to process the orders”

fi

if 命令也可以不和 test 命令一起使用,它可以根據命令返回的狀態代碼進行執行相關的任務。

if rm “$TEMPFILE” ; then

printf “%s/n” “$SCRIPT:temp file deleted”

else

printf “%s - status code %d/n” /

“ $SCRIPT:$LINENO: unable to delete temp file” $? 2>&

fi

在 if 命令中嵌入複雜的命令會使指令碼語言難讀且難以調試。你應該避免這樣做。在這個例子中,如果 rm 命令運行失敗,則它先顯示自己的提示資訊,接著顯示指令碼中的資訊。儘管在 if 命令內部也可以聲明變數,但是它很難確定那個變數存在,那個不存在。

case 命令

case 命令進行模板匹配測試,如果值和某個模板匹配,則執行相應的命令。變數逐個進行測試。

和 elif 命令不同,測試的狀態代碼來自同一個命令, case 測試變數的值。如果測試字串的值, case 命令比 elif 命令更好。

每一個 case 分支都必須用一對分號(;;)進行分割。如果沒有分號, Bash 會執行下一個分支並報錯。

printf “%s -> “ “1 = delete, 2 = archive.  Please choose one”

read REPLY

case “$REPLY” in

1) rm “$TEMPFILE” ;;

2) mv “$TEMPFILE” “$TEMPFILE.old” ;;

*) printf “%s/n” “$REPLY was not one of the choices” ;;

esac

星號表示所有沒有匹配模板的條件所執行的任務。雖然這是可選的,但是好的設計應該有一個這樣的寫法,即使裡面是一個空語句(:)也是好的。

模板匹配規則遵循 globbing 規則,參考前一張傑的內容。例如:豎條可以分開多個模板。

case 同其他電腦語言不一樣,不會跟著執行。當一個選擇了一個條件,則其他 case 不會執行。

while 迴圈

有幾個命令都可以實現重複執行一組命令。

while 命令根據測試條件執行封閉在 while 命令中命令組。如果命令失敗,則在 while 命令中的命令組不執行。

printf “%s/n” “Enter the names of companies or type control-d”

while read -p “Company ?” COMPANY; do

if test -f “orders_$COMPANY.txt” ; then

printf “%s/n” “There is an order file from this company”

else

printf “%s/n” “There are no order files from this company”

fi

done

while 命令使用 done 命令結束。不是你也許認為的 elihw 這樣的命令。

使用 true 命令作為測試條件, while 命令會無限迴圈下去,因為 true 總是返回成功,迴圈無疑會一直下去。

printf “%s/n” “Enter the names of companies or type quit”

while true ; do

read -p “Company ?” COMPANY

if [ “$COMPANY” = “quit” ] ; then

break

elif test -f “orders_$COMPANY.txt” ; then

printf “%s/n” “There is an order file from this company”

else

printf “%s/n” “There are no order files from this company”

fi

done

一個 while 迴圈可以使用 break 命令提前停止。在到達 break 命令後, Bash 會跳出迴圈並執行迴圈外的第一條命令。

break 後面可以跟著一個數字,表示跳出幾層迴圈。例如:

break 2

跳出 2 層迴圈。

和 break 對應的是 continue 命令,它會對後面的命令忽略,從頭開始從新迴圈。 continue 命令後面也可以跟一個數字表示跳到哪一層的迴圈。

until 迴圈

和 while 迴圈對應的是 until 迴圈命令, until 迴圈是直到測試條件成功才停止執行封閉在 until 語句中命令組,其他基本上和 until 命令相同。它相當於 while !。

until test -f “$INVOICE_FILE” ; do

printf “%s/n” “Waiting for the invoice file to arrive...”

sleep 30

done

將 false 和 until 一起使用可以建立無限迴圈, break 和 continue 命令同樣也可以用於 until 迴圈命令。

for 迴圈命令

標準的伯恩 for in loop 是變數在這兒檔案。 for 命令將一系列值分別放入變數中然後執行包含的命令。

for FILE_PREFIX in order invoice purchase_order; do

if test -f “$FILE_PREFIX””_vendor1.txt” ; then

printf “%s/n” “There is a $FILE_PREFIX file from vendor 1...”

fi

done

如果 in 後面的參數沒有,則 for 在外殼指令碼中參數中進行迴圈。

break 和 continue 命令可以用於 for 迴圈。

因為其他外殼的特性, for 迴圈不是通用的。

嵌入 let 命令((( .. )))

let 命令判斷如果運算式是 0 則返回狀態代碼 1 ,如果運算式不為 0 ,則返回 0 。和 test 命令可以使用一對方括弧來表示更容易閱讀一樣, let 命令也有更容易閱讀的表示,使用雙括弧。

下面的列表 7.1 樣本使用了 for 迴圈嵌入 let 命令的表達方式:

列表 7.1

#!/bin/bash

# forloop.sh: Count from 1 to 9

for (( COUNTER=1; COUNTER<10; COUNTER++ )) ; do

printf “The counter is now %d/n” “$COUNTER”

done

exit 0

當迴圈開始時,執行雙括弧中的第一個運算式,每次迴圈開始執行第三個運算式,並檢查第二個運算式,當第二個運算式返回 false ,迴圈結束。

$ bash forloop.sh

The counter is now 1

The counter is now 2

The counter is now 3

The counter is now 4

The counter is now 5

The counter is now 6

The counter is now 7

The counter is now 8

The counter is now 9

命令組( {..} )

命令可以使用大括弧組合到一個組內。

ls -1 | {

while read FILE ; do

echo “$FILE”

done

}

在本執行個體中, ls 命令的結果成為組命令的輸入。

$ test -f orders.txt && { ls -l orders.txt ; rm orders.txt; } /

|| printf “no such file”

如果檔案 orders.txt 存在,檔案顯示出來,接著被刪除。否則顯示“ no such file ”。在大括弧中的命令需要分號進行分割。

命令也可以使用子外殼進行分組,子外殼將在第九章進行討論。

report.bash :報表格式化

report.bash 是一個用來給銷售數字建立報表的指令碼程式。銷售數字檔案有產品名稱、本國銷售數、外國銷售數來組成。例如: report.bash 把下面的報表

binders 1024 576

pencils 472  235

rules 311  797

stencils 846 621

轉換為

Report created on Thu Aug 22 18:27:07 EDT 2002 by kburtch

Sales Report

Product          Country     Foreign       Total     Average

——                 ——        ——        ——        ——

binders             1024         576        1600         800

pencils              472         235         707         353

rules                311         797        1108         554

stencils             846         621        1467         733

——                 ——        ——        ——        ——

Total number of products: 4

End of report

列表 7.2 report.bash

!/bin/bash

#

# report.bash: simple report formatter

#

# Ken O. Burtch

# CVS: $Header$

# The report is read from DATA_FILE.  It should contain

# the following columns:

#

#   Column 1: PRODUCT = Product name

#   Column 2: CSALES  = Country Sales

#   Column 3: FSALES  = Foreign Sales

#

# The script will format the data into columns, adding total and

# average sales per item as well as a item count at the end of the

# report.

# Some Linux systems use USER instead of LOGNAME

if [ -z “$LOGNAME” ] ; then           # No login name?

declare –rx LOGNAME=”$USER”        # probably in USER

fi

shopt -s -o nounset

# Global Declarations

declare -rx SCRIPT=${0##*/}           # SCRIPT is the name of this script

declare -rx DATA_FILE=”report.txt”    # this is raw data for the report

declare -i  ITEMS=0                   # number of report items

declare -i  LINE_TOTAL=0              # line totals

declare -i  LINE_AVG=0                # line average

declare     PRODUCT                   # product name from data file

declare -i  CSALES                    # country sales from data file

declare -i  FSALES                     # foreign sales from data file

declare -rx REPORT_NAME=”Sales Report” # report title

# Sanity Checks

if test ! -r “$DATA_FILE” ; then

printf “$SCRIPT: the report file is missing—aborting/n” >&2

exit 192

fi

# Generate the report

printf “Report created on %s by %s/n” “`date`” “$LOGNAME”

printf “/n”

printf “%s/n” “$REPORT_NAME”

printf “/n”

printf “%-12s%12s%12s%12s%12s/n” “Product” “Country” “Foreign” “Total” “Average”

printf “%-12s%12s%12s%12s%12s/n” “——” “——” “——” “——” “——”

{ while read PRODUCT CSALES FSALES ; do

let “ITEMS+=1”

LINE_TOTAL=”CSALES+FSALES”

LINE_AVG=”(CSALES+FSALES)/2”

printf “%-12s%12d%12d%12d%12d/n” “$PRODUCT” “$CSALES” “$FSALES” /

“ $LINE_TOTAL” “$LINE_AVG”

done } < $DATA_FILE

# Print report trailer

printf “%-12s%12s%12s%12s%12s/n” “——” “——” “——” “——” “——”

printf “Total number of products: %d/n” “$ITEMS”

printf “/n”

printf “End of report/n”

exit 0

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.