linux_shell筆記(八)

<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

Bash 程序設計

控制結構:控制結構有 if...then for...in while until case 語句 。與控制結構配合使用的還有breakcontinue 語句也可用於調整shell 腳本中的命令執行順序。

if...then

if test-command

then

command

fi

test 內置命令:if 測試test-command 返回狀態,並基於這個狀態轉移控制。if 語句的結束由fi 標記。

echo -n “word 1:”

read word1

echo -n “word 2:”

read word2

if test “$word1” = ”$word2”

then

echo “Match”

fi

echo “End of program.”

 

上面echo 利用'-n' 選項來表示輸出echo 參數後不用換行。

bashtest 是一個內置命令,也就是它是shell 的一部分。同時還有一個單獨的工具test 。通常某個命令的工具版本不存在,而內置命令版本可用時,使用後者。test 測試語句中使用的'=' 兩邊的空白字符不可以省略。

 

test 的操作符'-eq' 比較兩個整數,特殊變量$# 表示命令行參數的個數。

if test $# -eq 0

then

echo “You must supply at least one argument.”

exit 1

fi

echo “Program running.”

可以利用'info test' 得到完整的test 用法信息:

測試文件類型:'-b FILE' '-c FILE' '-d FILE' '-f FILE' '-h FILE' '-L FILE' '-p FILE' '-S FILE' ‘-t FD’

訪問權限測試:'-g FILE' '-k FILE' '-r FILE' '-u FILE' '-w FILE' '-x FILE' '-O FILE'

'-G FILE'

文件特徵比較:'-e FILE' '-s FILE' 'FILE1 -nt FILE2' 'FILE1 -ot FILE2'

'FILE1 -ef FILE2'

字符串測試:'-z STRING' '-n STRING' 'STRING' 'STRING1 = STRING2'

'STRING1 != STRING2'

整數測試:'ARG1 -eq ARG2' 'ARG1 -ne ARG2' 'ARG1 -lt ARG2' 'ARG1 -le ARG2'

'ARG1 -gt ARG2' 'ARG1 -ge ARG2'

測試條件連接詞:'! EXPR ' 'EXPR1 -a EXPR2 ' 'EXPR1 -o EXPR2 '( 注意中間的空白符號)

[]test 同義 可以把test 的參數用方括號括起來,以代替在腳本中使用關鍵字test 。括號兩邊必須有空白符

 

if [ $# -eq 0 ]

then

ehco “Usage : chkarg2 argument...” 1>&2

exit 1

fi

echo “Program running.”

exit 0

 

用法信息 上面例子顯示的信息稱爲用法信息,使用 '1>&2' 標記可把這個輸出重定位到標準錯誤輸出 。在腳本正常運行後一般退出狀態爲 0 ,如果退出狀態非 0 表示遇到一個錯誤。

 

if...then...else

if test-command

then

commands

else

commands

fi

if test-command;then

commands

else

commands

fi

 

下面的shell 腳本,參數爲文件名,該腳本將文件內容顯示在終端上。如果第一個參數爲'-v' ,該腳本將使用less 命令分頁顯示。

if [ $# -eq 0 ]

then

echo “Usage: out [-v] filename...” 1>&2

exit 1

fi

if [ “$1” = “-v” ]

then

shift

less -- “$@”

else

cat -- “$@”

fi

在上面的腳本中,cat less 命令的 '--' 參數通知調用這些工具的命令行它後面再沒有選項了,即不再把 '--' 後面以連字符開頭的參數作爲選項。‘ rm -- -example.sh’( 刪除文件 -example.sh).

shift set 對位置參數的操作。

 

if … then … elif

if test-command

then

commands

elif test-command

then

commands

else

commands

fi

示例:

echo -n “Word 1:”

read word1

echo -n “Word 2:”

read word2

echo -n “Word 3:”

read word3

 

if [ “$word1” = “$word2” -a “$word1” = “$word3” ]

then

echo “Match : words 1,2 & 3”

elif [ “$word1” = “$word2” ]

then

echo “Match : word 1 & 2”

elif [ “$word2” = “$word3” ]

then

echo “Match : word 2 & 3”

else

echo “No Match”

fi

上面的腳本第一個if 語句使用布爾操作符 AND(-a) 作爲 test 的一個參數。注意 '=' '[' ']' 兩邊的空白符不可省略。

1 #!/bin/bash

2 # Identify links to a file

3 # Usage: lnks file [directory]

4 if [ $# -eq 0 -o $# -gt 2 ]

5 then

6 echo "Usage:lnks file [directory]" 1>&2

7 exit 1

8 fi

9

10 if [ -d "$1" ]

11 then

12 echo "First argument cannot be a directory." 1>&2

13 exit 1

14 else

15 file="$1"

16 fi

17 if [ $# -eq 1 ]

18 then

19 directory="."

20 elif [ -d "$2" ]

21 then

22 directory="$2"

23 else

24 echo "Optional second argument must be a directory." 1>&2

25 echo "Usage:lnks file [directory]." 1>&2

26 exit 1

27 fi

28

29 #check that file exists and is a regular file:

30 if [ ! -f "$file" ]

31 then

32 echo "lnks:$file not found or special file" 1>&2

33 exit 1

34 fi

35 #check link count on file

36 set -- $(ls -l "$file")

37 linkcnt=$2

38 if [ "$linkcnt" -eq 1 ]

39 then

40 echo "lnks : no other hard links to $file" 1>&2

41 exit 0

42 fi

43 #get the inode of the given file

44 set $(ls -i "$file")

45

46 inode=$1

47 #find and print the files with that incite number

48 echo "lnks:using find to search for links..." 1>&2

49 find "$directory" -xdev -inum $inode -print

50

黑體黃底命令行使用了命令替換 $(command)

確定shell ,用法消息,註釋,測試參數。

內置命令 set 使用命令替換機制把位置參數設置爲 ls -l 命令的輸出。 輸出的第二個字段就是鏈接的數目,因此就將linkcnt 就被設置爲$2set 中的參數'--' 防止把'-ls -l'( 第一個輸出字段是對文件的訪問許可,一般以字符‘-’ 開頭) 產生的信息作爲選項。

比較與文件名關聯的inode 來確定多個文件名是否鏈接到同一個文件,這是一種好方法。

find 工具用於查找滿足其參數所指定條件的文件,查找的位置由第一個參數($directory) 指定並搜索其所有子目錄。餘下的參數指定把值爲$inode 的文件的文件名送到標準輸出。由於不同文件系統中的文件可能擁有相同的inode 號,並且沒有鏈接,find 必須只查找同一文件系統中的目錄。參數-xdev 防止find 命令搜索其他文件系統。

可以使用 -x 選項來幫助調試腳本。該選項使得 shell 執行每條命令前把命令顯示出來。

可以在帶參數'-x'shell 中運行上面的shell 腳本,在執行每條命令前先將該命令顯示出來。要麼爲當前shell 設置'-x' 選項( 使用 set -x 命令) ,以使所有腳本的命令在執行前都顯示出來。要麼只在當前執行腳本中通過shell 使用'-x' 選項。

>bash -x lnks.sh example

腳本中的每條命令前都會加上PS4 變量的值,默認是加號'+'. 因此可通過該值來區分是腳本輸出還是調試輸出。

通過在腳本的開始位置加入帶有 '-x' 參數的 set ,就可以設置運行該腳本的 shell 'set -x'

若要關閉調試則引應使用命令:'set +x'

'set -x' 'set -xtrace'

控制結構for...in 的語法如下:

 

for loop-index in argument-list

do

commands

done

依照argument-list 中的每個參數( 將對應的每一個參數賦值給loop-index 變量) 重複執行dodone 語句之間的命令。

例子1

1 for fruit in apples oranges pears bananas

2 do

3 echo "$fruit"

4 done

例子2

1 for i in *

2 do

3 if [ -d "$i" ]

4 then

5 echo "$i"

6 fi

7 done

'*' 匹配當前目錄下所有可見文件名,與運行該 shell 腳本的當前目錄有關,而與 shell 腳本所在目錄無關。

 

for 控制結構語法如下:

for loop-index

do

commands

done

 

loop-index 用命令行參數中的每個參數值取代 ,重複執行dodone 之間的語句。除了參數來源不同,該結構語法同'for...in' 結構語法。

例子:

>cat -n for_test.sh

1 for arg

2 do

3 echo "$arg"

4 done

5

>chmod a+rwx for_test.sh

>./for_test.sh example example2 example4 example6

'for arg' 隱含表示 'for arg in “$@”' ,這裏將” $@” 擴展爲一個命令行參數列表, 如” $1””$2””$3” 等。

>cat -n whos.sh

1 #!/bin/bash

2 # adapted from finger.sh by Lee Sailer

3 # UNIX/WORLD

4

5 if [ $# -eq 0 ]

6 then

7 echo "Usage: whos id ..." 1>&2

8 exit 1

9 fi

10

11 for id

12 do

13 gawk -F: ' {print $1,$5} ' /etc/passwd |

14 grep -i "$id"

15 done

16

>./whos.sh root “terry Zeng”

腳本whos 演示了for 結構中$@ 符號所代表的含義。whos 腳本後面可以跟一個或幾個用戶名作爲參數,執行whos 腳本就可以將這些與用戶相關的信息顯示出來。

在這個腳本中的 for 循環中隱含的 $@ 的使用特別有效果,因爲它使得 for 循環可以把帶空格的參數視爲一個單獨的參數 ( 如參數” terry Zeng”)

對於每個命令行參數,whos 搜索/etc/passwd 文件。在for 循環結構中gawk 的作用是從文件/etc/passwd 的行中提取第一個($1) 和第5($5) 字段的內容。第1 個和第5 個字段的內容通過一個管道傳遞給grepgrep 在其輸入中搜索內容爲$id($id 就是輸入的命令行參數) 的對象。選項'-i' 的作用是讓grep 在搜索過程中忽略其他的事件,grep 在其輸入中按照每行的格式顯示包含$id 內容的對象。

行末的| 即便在管道標誌後面還有換行,管道也能照常工作。

 

while

while 控制結構語法如下:

while test-command

do

commands

done

只要測試條件的返回值爲真,while 結構語句就要執行dodone 語句之間的命令。

 

例子:

>cat -n count.sh

1 #!/bin/bash

2 number=0

3 while [ "$number" -lt 10 ]

4 do

5 echo -n "$number"

6 ((number += 1))

7 done

8 echo

test 內置命令:腳本使用了'-lt' 來執行數值比較測試。對於數值比較測試由以下幾種測試選項:'-ne','-eq','-gt','-ge','-lt','-le' 。對於字符串的比較可以用'=' 或者'!=' 來進行測試比較。

使用 '-n' 選項用來防止 echo 在其輸出之後輸出換行。最後的 echo 使腳本在標準輸出上輸出一個新的字符行。 ((number += 1)) 賦值表達式。

例子:

1 #!/bin/bash

2 # remove correct spellings from aspell output

3

4 if [ $# -ne 2 ]

5 then

6 echo "Usage spell_check.sh file1 file2" 1>&2

7 echo "file1 list of correct spellings" 1>&2

8 echo "file2 file to be checked" 1>&2

9 exit 1

10 fi

11

12 if [ ! -r "$1" ]

13 then

14 echo "spell ..check; $1 is not readable" 1>&2

15 exit 1

16 fi

17

18 if [ ! -r "$2" ]

19 then

20 echo "spell ..check; $2 is not readable" 1>&2

21 exit 1

22 fi

23

24 aspell -l < "$2" |

25 while read line

26 do

27 if ! grep "^$line$" "$1" > /dev/null

28 then

29 echo $line

30 fi

31 done

32

上面的腳本中'-r' 參數判斷一個文件是否是可讀的,括號裏的感嘆號是對跟着的操作符進行相反的運算,與'-r' 參數聯合起來用以判斷一個文件是否是不可讀的。

上面的腳本把aspell 腳本的輸出( 使用參數-l 可以讓aspell 腳本把所檢查出的錯誤單詞的列表送到標準輸出上) 通過一個管道送到while 結構的標準輸入上,while 結構從他的標準輸入上一次讀入一行內容( 每行只有一個單詞) 。只要測試條件( 也就是read line) 能從標準輸入上得到一個單詞,那麼它就返回一個true 狀態。

while 循環中if 語句用來檢測grep 條件測試的返回值,grep 是用來判斷被讀取行是否在用戶的正確單詞列表中。grep 搜索的模式( 也就是$line) 前後都有特殊字符,這些字符分別用來指明一行的開始和結束( 分別是'^''$''^$line$' 理解爲 '^---$line---$' 其中 '^' 代表行的開始,第一個 '$' 代表對變量 'line' 的引用,第二個 '$' 代表行的結束 ) 這些特殊符號的作用是確保grep 搜索時只有當變量$line 的內容與用戶輸入的正確單詞列表的一整行的內容相同時才形成匹配。grep 的標準輸出被重定向到文件/dev/null 中,因爲我們不關心輸出結果,只關心返回狀態。

' if ! grep "^$line$" "$1" > /dev/null ' 可以替換爲 ' if ! grep -qw “$line” “$1” ' 其中'-q' 抑制了grep 的輸出,這樣只返回退出狀態。'-w' 使得grep 只匹配整個單詞。

 

until

untilwhile 語句的語法結構相似。區別僅在於條件語句的測試位置:一個在語句的開始測試,一個在語句的結束測試。

until test-commands

do

commands

done

例子:

1 #! /bin/bash

2 secretname=jenny

3 name=noname

4 echo "Try to guess the secret name!"

5 echo

6 until [ "$name" = "$secretname" ]

7 do

8 echo -n "Your guess:"

9 read name

10 done

11 echo "Very good."

12

例子:

1 #!/bin/bash

2 # UNIX/WORLD

3 trap ' ' 1 2 3 18

4 stty -echo

5 echo -n "Key: "

6 read key_1

7 echo

8 echo -n "Again:"

9 read key_2

10 echo

11 key3=

12 if [ "$key_1" = "$key_2" ]

13 then

14 tput clear

15 until [ "$key_3" = "$key_2" ]

16 do

17 read key_3

18 done

19 else

20 echo "locktty: keys do not match" 1>&2

21 fi

22 stty echo

23

如果運行上面的例子而忘記了腳本密碼,可以登錄另一個虛擬終端並終止該進程。

trap 內置命令:利用 trap 內置命令可以防止用戶通過發送中斷的方式來終止腳本的運行。通過捕獲信號 18 可以保證用戶無法用 CONTROL+Z 組合鍵來掛起運行該腳本的進程。 stty -echo 命令來防止終端把鍵盤輸入的字符顯示出來 ,這樣可以保證輸入的密碼不被顯示出來。

breakcontinue :同c 語言可以用來中斷for while until 語句的執行

1 #!/bin/bash

2 for index in 1 2 3 4 5 6 7 8 9

3 do

4 if [ $index -le 3 ]

5 then

6 echo "continue"

7 continue

8 fi

9 echo $index

10 if [ $index -ge 8 ]

11 then

12 echo "break"

13 break

14 fi

15 done

16

 

case :一種多分支選擇機制,同C 語言。

case test-string in

pattern-1)

commands-1

;;

pattern-2)

commands-2

;;

pattern-3)

commands-3

;;

...

esac

case 結構中的匹配類型類似於一個模糊文件引用。

字符‘*’: 匹配任意字符串,用作默認的case 匹配。

字符‘?’: 匹配 單個字符。

[…]: 定義一個字符類,對處於方括號中的每個字符依次進行單字符匹配;兩個字符之間的連字符用來指定字符範圍。

| : 分離帶有選擇的選項,這些選項滿足case 結構的一個特別的分支。

例子:

1 echo -n "Enter A,B,or C:"

2 read letter

3 case "$letter" in

4 a|A)

5 echo "You Entered A"

6 ;;

7 b|B)

8 echo "You Entered B"

9 ;;

10 c|C)

11 echo "You Entered C"

12 ;;

13 *)

14 echo "You did not enter A,B,or C"

15 ;;

16 esac

例子:

1 #!/bin/bash

2

3 echo -e "/n COMMAND MENU/n"

4 echo "a.Current date and time"

5 echo "b.Users currently logged in"

6 echo "c.Name of the working directory"

7 echo -e "d.Contents of the working directory/n"

8 echo -n "Enter a,b,c,or d:"

9 read answer

10 echo

11 case "$answer" in

12 a)

13 date

14 ;;

15 b)

16 who

17 ;;

18 c)

19 pwd

20 ;;

21 d)

22 ls

23 ;;

24 *)

25 echo "There is no selection:$answer"

26 ;;

27 esac

'echo -e' 選項 -e 使 echo 把後面的 '/n' 解釋爲一個換行符,如果 echo 後面不加這個參數 '-e' echo 就會輸出兩個字符 '/n' ,而不是一個空行。參數 '-e' 使得 echo 解釋由反斜槓 '/' 轉義的字符。 帶有反斜槓的字符一定要引起來,否則反斜槓就要由 shell 來解釋而不會傳到 echo echo 解釋。

 

例子:

>cat -n safedit

1 #!/bin/bash

2 PATH=/bin:/usr/bin

3 script=$(basename $0)

4 case $# in

5 0)

6 vim

7 exit 0

8 ;;

9 1)

10 if [ ! -f "$1" ]

11 then

12 vim "$1"

13 exit 0

14 fi

15 if [ ! -r "$1" -o ! -w "$1" ]

16 then

17 echo "$script:check permissions on $1" 1>&2

18 exit 1

19 else

20 editfile="$1"

21 fi

22 if [ ! -w "." ]

23 then

24 echo "$script:backup cannont be"/

25 "created in the working directory" 1>&2

26 exit 1

27 fi

28 ;;

29 *)

30 echo "Usage:$script [file-to-edit]" 1>&2

31 exit 1

32 ;;

33 esac

34

35 tempfile=/tmp/$$.$script

36 cp $editfile $tempfile

37 if vim $editfile

38 then

39 mv $tempfile bak.$(basename $editfile)

40 echo "$script:backup file created"

41 else

42 mv $tempfile editerr

43 echo "$script:edit error--copy of" /

44 "original file is in editerr"1>&2

45 fi

46

>chmod u+x safedit

>./safedit example.sh

再另一個控制檯上進入tmp 目錄可以查看到一個nxxn.safedit 的臨時文件,回到原來的控制檯退出vim 編輯器,查看當前控制檯當前工作目錄下會生成一個備份文件格式bak.example.sh 。再打開新的控制檯查看/tmp 目錄發現最開始生成的臨時文件已經被刪除。

>ln safedit safedit.ln

>./safedit.ln example.sh

注意觀察該命令行運行的過程,生成的中間文件,最後結果與上面文字描述的不同。

設置 PATH 變量:該腳本設置了 PATH 變量,目的是爲了保證腳本中執行的命令是系統目錄中的標準命令。避免了用戶自己可能設置的 PATH 包含自己定義的目錄,而在該自定義目錄下,用戶可能編寫了一些與腳本中調用命令同名的腳本或程序。

程序名: basename 去掉前導的目錄部分後打印名稱,因此腳本中將 script 設置爲運行腳本的基本名稱。通過命令替換實現。 $0 存儲腳本被調用時的命令。好處在於對腳本進行重命名或則創建鏈接後運行該腳本會得到正確的提示信息。

給臨時文件命名:腳本中設置變量 tempfile 爲臨時文件名,以 shell 進程的 PID 號作爲開始並以腳本的名字作爲結束。使用 PID 號是爲了確保文件名的唯一性,腳本名字附在臨時文件名後面,爲了讓用戶知道其來源。 PID 號放在前面是由於一些老版本 unix 上對文件名有長度限制,而 PID 號可以確保其唯一性,所以放在前面避免由於文件名字長的限制而被切去。

測試條件:腳本中使用了一個測試條件 vim $editfile 。測試 vim 編輯器編輯文件完成後返回的 exit 代碼, if 控制結構就是利用這個 exit 代碼來決定分支。成功則返回 0 then 被執行;不成功則返回非 0 else 語句被執行。

 

select :首先顯示一個菜單,然後根據用戶的選擇給變量賦予相應的值,最後執行一系列命令。

select varname [in arg ….]

do

command

done

 

例子:

1 #! /bin/bash

2 PS3= 'Choose your favorite fruit from these from these possibilities:'

3 select FRUIT in apple banana blueberry kiwi orange watermelon STOP

4 do

5 if [ "$FRUIT" = "" ];then

6 echo -e "Invalid entry./n"

7 continue

8 elif [ "$FRUIT " = STOP ];then

9 echo "Thanks for playing."

10 break

11 fi

12 echo "You Chose $FRUIT as your favorite."

13 echo -e "That is choice number $REPLY ./n"

14 done

15

PS3 :是 select 特有的提示符,在 select 語句輸出菜單後,就會顯示出 PS3 的值。 slect 不斷的發出 PS3 提示並按照用戶的輸入執行命令,直到有事情使其停止。

select varname 設置爲輸入的值 ( 例子中是變量 FRUIT) ,同時將用戶的響應存儲在鍵盤變量 REPLY 中。如果用用戶非法輸入, shell varname 設置爲空 ($FRUIT)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章