Linux Shell常用技巧(十二)
二十三. Bash 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 $?
?? ?0???
?? ?在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命令。
?? ?The last argument is d
總結
以上是生活随笔為你收集整理的Linux Shell常用技巧(十二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux Shell常用技巧(十一)
- 下一篇: Linux Shell高级技巧(一)