1. 程式人生 > >Linux Shell常用技巧(十二)

Linux Shell常用技巧(十二)

  1.  讀取使用者變數:

read命令是用於從終端或者檔案中讀取輸入的內建命令,read命令讀取整行輸入,每行末尾的換行符不被讀入。在read命令後面,如果沒有指定變數名,讀取的資料將被自動賦值給特定的變數REPLY。下面的列表給出了read命令的常用方式:

命令格式描述

read answer從標準輸入讀取輸入並賦值給變數answer。

read first last從標準輸入讀取輸入到第一個空格或者回車,將輸入的第一個單詞放到變數first中,並將該行其他的輸入放在變數last中。

read從標準輸入讀取一行並賦值給特定變數REPLY。

read -a arrayname把單詞清單讀入arrayname的數組裡。

read -p prompt列印提示,等待輸入,並將輸入儲存在REPLY中。

read -r line允許輸入包含反斜槓。

見下面的示例(綠色高亮部分的文字為控制檯手工輸入資訊):

    /> read answer   #等待讀取輸入,直到回車後表示輸入完畢,並將輸入賦值給變數answer

 Hello#控制檯輸入Hello

 /> echo $answer#列印變數

Hello

#等待一組輸入,每個單詞之間使用空格隔開,直到回車結束,並分別將單詞依次賦值給這三個讀入變數。

 /> read one two three

 1 2 3 #在控制檯輸入1 2 3,它們之間用空格隔開。

 /> echo "one = $one, two = $two, three = $three"

one = 1, two = 2, three = 3

 /> read #等待控制檯輸入,並將結果賦值給特定內建變數REPLY。

 This is REPLY#在控制檯輸入該行。

 /> echo $REPLY #列印輸出特定內建變數REPLY,以確認是否被正確賦值。

This is REPLY

 /> read -p "Enter your name: "

#輸出"Enter your name: "文字提示,同時等待輸入,並將結果賦值給REPLY。

Enter you name: stephen#在提示文字之後輸入stephen

 /> echo $REPLY

stephen

#等待控制檯輸入,並將輸入資訊視為陣列,賦值給陣列變數friends,輸入資訊用空格隔開陣列的每個元素

 /> read -a friends

 Tim Tom Helen

 /> echo "I have ${#friends} friends"

I have 3 friends

 /> echo "They are ${friends[0]}, ${friends[1]} and ${friends[2]}."

They are Tim, Tom and Helen.

 2.  狀態判斷:

test是Shell中提供的內建命令,主要用於狀態的檢驗,如果結果為0,表示成功,否則表示失敗。見如下示例:

    /> name=stephen

    /> test $name != stephen

    /> echo $?

1

需要注意的是test命令不支援Shell中提供的各種萬用字元,如:

    /> test $name = [Ss]tephen

    /> echo $?

1

test命令還可以中括號予以替換,其語義保持不變,如:

    /> [ $name = stephen ]

    /> echo $?

在Shell中還提供了另外一種用於狀態判斷的方式:[[ expr ]],和test不同的是,該方式中的表示式支援萬用字元,如:

    /> name=stephen

    /> [[ $name == [Ss]tephen ]]

    /> echo $?

0

#在[[ expression ]]中,expression可以包含&&(邏輯與)和||(邏輯或)。

    /> [[ $name == [Ss]tephen && $friend == "Jose" ]]

    /> echo $?

1

 /> shopt -s extglob#開啟Shell的擴充套件匹配模式。

    /> name=Tommy

# "[Tt]o+(m)y"的含義為,以T或t開頭,後面跟著一個o,再跟著一個或者多個m,最後以一個y結尾。

 /> [[ $name == [Tt]o+(m)y ]] 

    /> echo $?

0

在Shell中還提供了let命令的判斷方式: (( expr )),該方式的expr部分,和C語言提供的表示式規則一致,如:

    /> x=2

    /> y=3

    /> (( x > 2 ))

    /> echo $?

1

    /> (( x < 2 ))

    /> echo $?

0

    /> (( x == 2 && y == 3 ))

    /> echo $?

0

    /> (( x > 2 || y < 3 ))

    /> echo $?

1

    下面的表格是test命令支援的操作符:

判斷操作符判斷為真的條件

字串判斷 

[ stringA=stringB ]stringA等於stringB

[ stringA==stringB ]stringA等於stringB

[ stringA!=stringB ]stringA不等於stringB

[ string ]string不為空

[ -z string ]string長度為0

[ -n string ]string長度不為0

邏輯判斷 

[ stringA -a stringB ]stringA和stringB都是真

[ stringA -o stringB ]stringA或stringB是真

[ !string ]string不為真

邏輯判斷(複合判斷) 

[[ pattern1 && pattern2 ]]pattern1和pattern2都是真

[[ pattern1 || pattern2 ]pattern1或pattern2是真

[[ !pattern ]]pattern不為真

整數判斷 

[ intA -eq intB ]intA等於intB

[ intA -ne intB ]intA不等於intB

[ intA -gt intB ]intA大於intB

[ intA -ge intB ]intA大於等於intB

[ intA -lt intB ]intA小於intB

[ intA -le intB ]intA小於等於intB

檔案判斷中的二進位制操作 

[ fileA -nt fileB ]fileA比fileB新

[ fileA -ot fileB ]fileA比fileB舊

[ fileA -ef fileB ]fileA和fileB有相同的裝置或者inode值

檔案檢驗 

[ -d $file ] or [[ -d $file ]]file為目錄且存在時為真

[ -e $file ] or [[ -e $file ]]file為檔案且存在時為真

[ -f $file ] or [[ -f $file ]]file為非目錄普通檔案存在時為真

[ -s $file ] or [[ -s $file ]]file檔案存在, 且長度不為0時為真

[ -L $file ] or [[ -L $file ]]file為連結符且存在時為真

[ -r $file ] or [[ -r $file ]]file檔案存在且可讀時為真

[ -w $file ] or [[ -w $file ]]file檔案存在且可寫時為真

[ -x $file ] or [[ -x $file ]]file檔案存在且可執行時為真

    注:在邏輯判斷(複合判讀中),pattern可以包含元字元,在字串的判斷中,pattern2必須被包含在引號中。

let命令支援的操作符和C語言中支援的操作符完全相同,如:

    +,-,*,/,%            加,減,乘,除,去模

    >>,<<                右移和左移

    >=,<=,==,!=      大於等於,小於等於,等於,不等於

    &,|,^                  按位與,或,非

    &&,||,!                邏輯與,邏輯或和取反

還有其含義和C語言等同的快捷操作符,如=,*=,/=,%=,+=,-=,<<=,>>=,&=,|=,^=。

    3.  流程控制語句:

    if語句格式如下:

#if語句的後面是Shell命令,如果該命令執行成功返回0,則執行then後面的命令。

    if command        

    then

        command

        command

    fi

#用test命令測試其後面expression的結果,如果為真,則執行then後面的命令。

    if test expression

    then

        command

    fi

#下面的格式和test expression等同

    if [ string/numeric expression ]

    then

        command

    fi

#下面的兩種格式也可以用於判斷語句的條件表示式,而且它們也是目前比較常用的兩種。

    if [[ string expression ]]

    then

        command

    fi

 if (( numeric expression ))#let表示式

    then

        command

    fi

見如下示例:

 /> cat > test1.sh #從命令列直接編輯test1.sh檔案。

    echo -e "Are you OK(y/n)? \c"

    read answer

 #這裡的$answer變數必須要用雙引號擴住,否則判斷將失敗。當變數$answer等於y或Y時,支援下面的echo命令。

    if [ "$answer" = y -o "$answer" = Y ]   

    then

        echo "Glad to see it."

    fi

    CTRL+D  

 /> . ./test1.sh

Are you OK(y/n)? y

Glad to see it.

上面的判斷還可以替換為:

 /> cat > test2.sh

    echo -e "Are you OK(y/n or Maybe)? \c"

    read answer

    # [[ ]]複合命令操作符允許其中的表示式包含元字元,這裡輸入以y或Y開頭的任意單詞,或Maybe都執行then後面的echo。

    if [[ $answer == [yY]* || $answer = Maybe ]]  

    then

        echo "Glad to hear it.

    fi

    CTRL+D

    /> . ./test2.sh

Are you OK(y/n or Maybe)? yes

Glad to hear it.

下面的例子將使用Shell中的擴充套件通配模式。

 /> shopt -s extglob#開啟該擴充套件模式

 /> answer="not really"

    /> if [[ $answer = [Nn]o?( way |t really) ]]

    > then

    >    echo "I am sorry."

    > fi

I am sorry.

 對於本示例中的擴充套件萬用字元,這裡需要給出一個具體的解釋。[Nn]o匹配No或no,?( way|t really)則表示0個或1個( way或t really),因此answer變數匹配的字串為No、no、Not really、not really、No way、no way。

 下面的示例使用了let命令操作符,如:

 /> cat > test3.sh

    if (( $# != 2 ))    #等同於 [ $# -ne 2 ]

 then

        echo "Usage: $0 arg1 arg2" 1>&2

        exit 1       #exit退出值為0-255之間,只有0表示成功。

 fi

    if (( $1 < 0 || $1 > 30 )) #等同於 [ $1 -lt 0 -o $1 -gt 30 ]

 then

        echo "arg1 is out of range."

        exit 2

    fi

    if (( $2 <= 20 ))     #等同於 [ $2 -le 20 ]

 then

        echo "arg2 is out of range."

    fi

    CTRL+D

    /> sh ./test3.sh

Usage: ./test3.sh arg1 arg2

 /> echo $?#Shell指令碼的退出值為exit的引數值。

1

 /> sh ./test3.sh 40 30

arg1 is out of range.

 /> echo $?

2

 下面的示例為如何在if的條件表示式中檢驗空變數:

 /> cat > test4.sh

    if [ "$name" = "" ]   #雙引號就表示空字串。

  then

        echo "name is null."

    fi

    CTRL+D

    /> . ./test4.sh

name is null.

 if/elif/else語句的使用方式和if語句極為相似,相信有程式設計經驗的人都不會陌生,這裡就不在贅述了,其格式如下:

    if command

then 

        command

    elif command

    then

        command

    else

        command

    fi

見如下示例指令碼:

 /> cat > test5.sh

    echo -e "How old are you? \c"

    read age

    if [ $age -lt 0 -o $age -gt 120 ]  #等同於 (( age < 0 || age > 120 ))

 then

        echo "You are so old."

    elif [ $age -ge 0 -a $age -le 12 ]    #等同於 (( age >= 0 && age <= 12 ))

 then

        echo "You are child."

    elif [ $age -ge 13 -a $age -le 19 ]#等同於 (( age >= 13 && age <= 19 ))

 then

        echo "You are 13--19 years old."

    elif [ $age -ge 20 -a $age -le 29 ]   #等同於 (( age >= 20 && age <= 29 ))

 then

        echo "You are 20--29 years old."

    elif [ $age -ge 30 -a $age -le 39 ]   #等同於 (( age >= 30 && age <= 39 ))

 then

        echo "You are 30--39 years old."

    else

        echo "You are above 40."

    fi

    CTRL+D

    /> . ./test5.sh

How old are you? 50

You are above 40.

 case語句格式如下:

    case variable in

    value1)

        command

        ;;           #相同於C語言中case語句內的break。

 value2)

        command

        ;;

    *)               #相同於C語言中switch語句內的default

       command

        ;;

    esac

見如下示例指令碼:

    /> cat > test6.sh

    #!/bin/sh

    echo -n "Choose a color: "

    read color

    case "$color" in

    [Bb]l??)

        echo "you select blue color."

        ;;

    [Gg]ree*)

        echo "you select green color."

        ;;

    red|orange)

        echo "you select red or orange."

        ;;

    *)

        echo "you select other color."

        ;;

    esac

    echo "Out of case command."

    /> . ./test6.sh

Choose a color: green

you select green color.

Out of case command.

   4.  迴圈語句:

Bash Shell中主要提供了三種迴圈方式:for、while和until。

 for迴圈宣告格式:

    for variable in word_list

    do

        command

    done

見如下示例指令碼:

 /> cat > test7.sh

    for score in math english physics chemist #for將迴圈讀取in後面的單詞列表,類似於Java的for-each。

    do

        echo "score = $score"

    done

    echo "out of for loop"

    CTRL+D

 /> . ./test7.sh

score = math

score = english

score = physics

score = chemist

out of for loop

 /> cat > mylist#構造資料檔案

 tom

    patty

    ann

    jake

    CTRL+D

 /> cat > test8.sh

    #!/bin/sh

    for person in $(cat mylist)   #for將迴圈讀取cat mylist命令的執行結果。

 do

        echo "person = $person"

    done

    echo "out of for loop."

    CTRL+D

    /> . ./test8.sh

person = tom

person = patty

person = ann

person = jake

out of for loop.

 /> cat > test9.sh

    for file in test[1-8].sh #for將讀取test1-test8,字尾為.sh的檔案

 do

        if [ -f $file ]     #判斷檔案在當前目錄是否存在。

 then

            echo "$file exists."

        fi

    done

    CTRL+D

    /> . ./test9.sh

test2.sh exists.

test3.sh exists.

test4.sh exists.

test5.sh exists.

test6.sh exists.

test7.sh exists.

test8.sh exists.

 /> cat > test10.sh

    for name in $*     #讀取指令碼的命令列引數陣列,還可以寫成for name的簡化形式。

 do

        echo "Hi, $name"

    done

    CTRL+D

    /> . ./test10.sh stephen ann

Hi, stephen

Hi, ann

 while迴圈宣告格式:

 while command#如果command命令的執行結果為0,或條件判斷為真時,執行迴圈體內的命令。

    do

        command

    done

見如下示例指令碼:

 /> cat > test1.sh

 num=0

    while (( num < 10 ))#等同於 [ $num -lt 10 ]

 do

        echo -n "$num "

        let num+=1

    done

    echo -e "\nHere's out of loop."

    CTRL+D

    /> . ./test1.sh

0 1 2 3 4 5 6 7 8 9 

Here's out of loop.

 /> cat > test2.sh

    go=start

    echo Type q to quit.

    while [[ -n $go ]] #等同於[ -n "$go" ],如使用該風格,$go需要被雙引號括起。

 do

        echo -n How are you.

        read word

        if [[ $word == [Qq] ]] #等同於[ "$word" = Q -o "$word" = q ]

  then

            echo Bye.

            go=       #將go變數的值置空。

        fi

    done

    CTRL+D

    /> . ./test2.sh

How are you. Hi

How are you. q

Bye.

 until迴圈宣告格式:

 until command #其判斷條件和while正好相反,即command返回非0,或條件為假時執行迴圈體內的命令。

 do

        command

    done

見如下示例指令碼:

 /> cat > test3.sh

    until who | grep stephen #迴圈體內的命令將被執行,直到stephen登入,即grep命令的返回值為0時才退出迴圈。

 do

        sleep 1

        echo "Stephen still doesn't login."

    done

    CTRL+D

 shift命令宣告格式:shift [n]

shift命令用來把指令碼的位置引數列表向左移動指定的位數(n),如果shift沒有引數,則將引數列表向左移動一位。一旦移位發生,被移出列表的引數就被永遠刪除了。通常在while迴圈中,shift用來讀取列表中的引數變數。

見如下示例指令碼:

 /> set stephen ann sheryl mark #設定4個引數變數。

 /> shift#向左移動引數列表一次,將stephen移出引數列表。

 /> echo $*

ann sheryl mark

 /> shift 2#繼續向左移動兩位,將sheryl和ann移出引數列表

 /> echo $*

mark

 /> shift 2#繼續向左移動兩位,由於引數列表中只有mark了,因此本次移動失敗。

 /> echo $*

mark

 /> cat > test4.sh

    while (( $# > 0 ))   #等同於 [ $# -gt 0 ]

 do

        echo $*

        shift

    done

    CTRL+D

    /> . ./test4.sh a b c d e

a b c d e

b c d e

c d e

d e

e

 break命令宣告格式:break [n]

和C語言不同的是,Shell中break命令攜帶一個引數,即可以指定退出迴圈的層數。如果沒有指定,其行為和C語言一樣,即退出最內層迴圈。如果指定迴圈的層數,則退出指定層數的迴圈體。如果有3層巢狀迴圈,其中最外層的為1,中間的為2,最裡面的是3。

見如下示例指令碼:

 /> cat > test5.sh

    while true

    do

        echo -n "Are you ready to move on?"

        read answer

        if [[ $answer == [Yy] ]]

        then

            break

        else

            echo "Come on."

        fi

    done

    echo "Here we are."

    CTRL+D

    /> . ./test5.sh

Are you ready to move on? y

Here we are

 continue命令宣告格式:continue [n]

和C語言不同的是,Shell中continue命令攜帶一個引數,即可以跳轉到指定層級的迴圈頂部。如果沒有指定,其行為和C語言一樣,即跳轉到最內層迴圈的頂部。如果指定迴圈的層數,則跳轉到指定層級迴圈的頂部。如果有3層巢狀迴圈,其中最外層的為3,中間的為2,最裡面的是1。

 /> cat  maillist#測試資料檔案maillist的內容為以下資訊。

stephen

ann

sheryl

mark

 /> cat > test6.sh

    for name in $(cat maillist)

    do

        if [[ $name == stephen ]]; then

            continue

        else

            echo "Hello, $name."

        fi

    done

    CTRL+D

    /> . ./test6.sh

Hello, ann.

Hello, sheryl.

Hello, mark.

 I/O重新定向和子Shell:

檔案中的輸入可以通過管道重新定向給一個迴圈,輸出也可以通過管道重新定向給一個檔案。Shell啟動一個子Shell來處理I/O重新定向和管道。在迴圈終止時,迴圈內部定義的任何變數對於指令碼的其他部分來說都是不看見的。

 /> cat > demodata#為下面的指令碼構造冊數資料

abc

def

ghi

 CRTL+D

 /> cat > test7.sh

    if (( $# < 1 ))     #如果指令碼引數的數量小於1,則給出錯誤提示後退出。

 then

        echo "Usage: $0 filename " >&2

        exit 1

    fi

    count=1

    cat $1 | while read line#引數一中的檔案被cat命令輸出後,通過管道逐行輸出給while read line。

 do

        let $((count == 1)) && echo "Processing file $1..." > /dev/tty#該行的echo將輸出到當前終端視窗。

 echo -e "$count\t$line"#將輸出行號和檔案中該行的內容,中間用製表符隔開。

 let count+=1

    done > outfile    #將while迴圈中所有的輸出,除了>/dev/tty之外,其它的全部輸出到outfile檔案。

 CTRL+D

 /> . ./test7.sh demodata#只有一行輸出,其餘的都輸出到outfile中了。

Processing file demodata...

 /> cat outfile

1       abc

2       def

3       ghi

 /> cat > test8.sh

    for i in 9 7 2 3 5 4

    do

        echo $i

    done | sort -n   #直接將echo的輸出通過管道重定向sort命令。

    CTRL+D

    /> . ./test8.sh

2

3

4

5

7

9

 5.  IFS和迴圈:

Shell的內部域分隔符可以是空格、製表符和換行符。它可以作為命令的分隔符用在例如read、set和for等命令中。如果在列表中使用不同的分隔符,使用者可以自己定義這個符號。在修改之前將IFS原始符號的值儲存在另外一個變數中,這樣在需要的時候還可以還原。

見如下示例指令碼:

 /> cat > test9.sh

    names=Stephen:Ann:Sheryl:John#names變數包含的值用冒號分隔。

 oldifs=$IFS #保留原有IFS到oldifs變數,便於後面的還原。

 IFS=":"

    for friends in $names   #這是遍歷以冒號分隔的names變數值。

 do

        echo Hi $friends

    done

    IFS=$oldifs       #將IFS還原為原有的值。

 set Jerry Tom Angela

    for classmates in $*    #再以原有IFS的值變數引數列表。

 do

        echo Hello $classmates

    done

    CTRL+D

    /> . ./test9.sh

Hi Stephen

Hi Ann

Hi Sheryl

Hi John

Hello Jerry

Hello Tom

Hello Angela

 6.  函式:

Shell中函式的職能以及優勢和C語言或其它開發語言基本相同,只是語法格式上的一些差異。下面是Shell中使用函式的一些基本規則:

1) 函式在使用前必須定義。

2) 函式在當前環境下執行,它和呼叫它的指令碼共享變數,並通過位置參量傳遞引數。而該位置參量將僅限於該函式,不會影響到指令碼的其它地方。

3) 通過local函式可以在函式內建立本地變數,該變數在出了函式的作用域之後將不在有效。

4) 函式中呼叫exit,也將退出整個指令碼。

5) 函式中的return命令返回函式中最後一個命令的退出狀態或給定的引數值,該引數值的範圍是0-256之間。如果沒有return命令,函式將返回最後一個Shell的退出值。

6) 如果函式儲存在其它檔案中,就必須通過source或dot命令把它們裝入當前指令碼。

7) 函式可以遞迴。

8) 將函式從Shell中清空需要執行:unset -f function_name。

9) 將函式輸出到子Shell需要執行:export -f function_name。

10) 可以像捕捉Shell命令的返回值一樣獲取函式的返回值,如$(function_name)。

Shell中函式的宣告格式如下:

 function function_name { command; command; }

見如下示例指令碼:

 /> cat > test1.sh

    function increment() {       #定義函式increment。

 local sum#定義本地變數sum。

        let "sum=$1+1"    

        return $sum    #返回值是sum的值。

 }

    echo -n "The num is "

    increment 5   #increment函式呼叫。

 echo $? #輸出increment函式的返回值。

 CTRL+D

    /> . ./test1.sh

The num is 6

 7.  陷阱訊號(trap):

在Shell程式執行的時候,可能收到各種訊號,有的來自於作業系統,有的來自於鍵盤,而該Shell在收到訊號後就立刻終止執行。但是在有些時候,你可能並不希望在訊號到達時,程式就立刻停止執行並退出。而是他能希望忽略這個訊號而一直在執行,或者在退出前作一些清除操作。trap命令就允許你控制你的程式在收到訊號以後的行為。

其格式如下:

    trap 'command; command' signal-number

    trap 'command; command' signal-name

    trap signal-number  

    trap signal-name

後面的兩種形式主要用於訊號復位,即恢復處理該訊號的預設行為。還需要說明的是,如果trap後面的命令是使用單引號括起來的,那麼該命令只有在捕獲到指定訊號時才被執行。如果是雙引號,則是在trap設定時就可以執行變數和命令替換了。

下面是系統給出的訊號數字和訊號名稱的對照表:

    1)SIGHUP 2)SIGINT 3)SIGQUIT 4)SIGILL 5)SIGTRAP 6)SIGABRT 7)SIGBUS 8)SIGFPE

    9)SIGKILL 10) SIGUSR1 11)SIGEGV 12)SIGUSR2 13)SIGPIPE 14)SIGALRM 15)SIGTERM 17)SIGCHLD

    18)SIGCONT 19)SIGSTOP ... ...

見如下示例指令碼:

 /> trap 'rm tmp*;exit 1' 1 2 15#該命令表示在收到訊號1、2和15時,該指令碼將先執行rm tmp*,然後exit 1退出指令碼。

 /> trap 2 #當收到訊號2時,將恢復為以前的動作,即退出。

 /> trap " " 1 2 #當收到訊號1和2時,將忽略這兩個訊號。

 /> trap - #表示恢復所有訊號處理的原始值。

 /> trap 'trap 2' 2#在第一次收到訊號2時,執行trap 2,這時將訊號2的處理恢復為預設模式。在收到訊號2時,Shell程式退出。

 /> cat > test2.sh

    trap 'echo "Control+C will not terminate $0."' 2   #捕獲訊號2,即在鍵盤上按CTRL+C。

 trap 'echo "Control+\ will not terminate $0."' 3 #捕獲訊號3,即在鍵盤上按CTRL+\。

 echo "Enter stop to quit shell."

    while true                 #無限迴圈。

    do

        echo -n "Go Go...."

        read

        if [[ $REPLY == [Ss]top ]]           #直到輸入stop或Stop才退出迴圈和指令碼。

       then

            break

        fi

    done

    CTRL+D

    /> . ./test2.sh

Enter stop to quit shell.

Go Go....^CControl+C will not terminate -bash.

^\Control+\ will not terminate -bash.

 stop

 8.  用getopts處理命令列選項:

這裡的getopts命令和C語言中的getopt幾乎是一致的,因為指令碼的位置參量在有些時候是失效的,如ls -lrt等。這時候-ltr都會被儲存在$1中,而我們實際需要的則是三個展開的選項,即-l、-r和-t。見如下帶有getopts的示例指令碼:

 /> cat > test3.sh

    #!/bin/sh

    while getopts xy options #x和y是合法的選項,並且將-x讀入到變數options中,讀入時會將x前面的橫線去掉。

 do

        case $options in

        x) echo "you entered -x as an option" ;;       

        y) echo "you entered -y as an option" ;;

        esac

    done

 /> ./test3.sh -xy

you entered -x as an option

you entered -y as an option

 /> ./test3.sh -x

you entered -x as an option

 /> ./test3.sh -b#如果輸入非法選項,getopts會把錯誤資訊輸出到標準錯誤。

./test3.sh: illegal option -- b

 /> ./test3.sh b#該命令不會有執行結果,因為b的前面有沒橫線,因此是非法選項,將會導致getopts停止處理並退出。

 /> cat > test4.sh

    #!/bin/sh

    while getopts xy options 2>/dev/null #如果再出現選項錯誤的情況,該重定向會將錯誤輸出到/dev/null。

 do

        case $options in

x) echo "you entered -x as an option" ;; 

        y) echo "you entered -y as an option" ;;

        \?) echo "Only -x and -y are valid options" 1>&2 # ?表示所有錯誤的選項,即非-x和-y的選項。

 esac

    done

    /> . ./test4.sh -g    #遇到錯誤的選項將直接執行\?)內的程式碼。

Only -x and -y are valid options

 /> . ./test4.sh -xg

you entered -x as an option

Only -x and -y are valid options

 /> cat > test5.sh

    #!/bin/sh

    while getopts xyz: arguments 2>/dev/null #z選項後面的冒號用於提示getopts,z選項後面必須有一個引數。

 do

        case $arguments in

        x) echo "you entered -x as an option." ;;

        y) echo "you entered -y as an option." ;;

        z) echo "you entered -z as an option."  #z的後面會緊跟一個引數,該引數儲存在內建變數OPTARG中。

 echo "\$OPTARG is $OPTARG.";

           ;;

        \?) echo "Usage opts4 [-xy] [-z argument]"

            exit 1 ;;

        esac

    done

    echo "The number of arguments passed was $(( $OPTIND - 1 ))" #OPTIND儲存一下將被處理的選項的位置,他是永遠比實際命令列引數多1的數。

 /> ./test5.sh -xyz foo

you entered -x as an option.

you entered -y as an option.

you entered -z as an option.

$OPTARG is foo.

The number of arguments passed was 2

 /> ./test5.sh -x -y -z boo

you entered -x as an option.

you entered -y as an option.

you entered -z as an option.

$OPTARG is boo.

The number of arguments passed was 4

 9.  eval命令與命令列解析:

eval命令可以對命令列求值,做Shell替換,並執行命令列,通常在普通命令列解析不能滿足要求時使用。

 /> set a b c d

    /> echo The last argument is \$$#

The last argument is $4

 /> eval echo The last argument is \$$##eval命令先進行了變數替換,之後再執行echo命令。

T