bash-shell高级编程--条件判断
條件判斷
每一個完整并且合理的程序語言都具有條件判斷的功能,并且可以根據條件判斷的結果做下一步處理,bash中有test命令,有各種中括號和圓括號操作,和if/then結構
條件測試結構
- 使用if/then結構判斷命令列表的退出碼狀態是否為0,如果成功的話,那么就執行接下來一個或多個命令。
- 有一個專有命令[(左括號,特殊字符),這個命令與test命令等價,并且處于效率上的考慮,這是一個內建命令,這個命令把它的參數作為表達式或者文件測試,并且根據比較的結果返回一個退出狀態碼(0表示真,1表示假)
- 在版本2.02的bash中,引入了[[....]]擴展測試命令,因為這種表現形式可能對某些語言的程序員來說更熟悉一些,注意[[]]是一個關鍵字,并不是一個命令,并且bash會將[[ $a -lt $b ]]看成單獨的元素,并且返回一個退出狀態碼。
if命令不僅可以測試中括號中的條件,可以測試任何命令
if cmp a b &> /dev/null # 禁止輸出. then echo "Files a and b are identical." else echo "Files a and b differ." fi# 非常有用的"if-grep"結構: # ------------------------ if grep -q Bash file then echo "File contains at least one occurrence of Bash." fiword=Linux letter_sequence=inu if echo "$word" | grep -q "$letter_sequence" # "-q" 選項是用來禁止輸出的. thenecho "$letter_sequence found in $word" elseecho "$letter_sequence not found in $word" fi## 執行成功,進到if條件判斷下 if COMMAND_WHOSE_EXIT_STATUS_IS_0_UNLESS_ERROR_OCCURRED then echo "Command succeeded." else echo "Command failed." fi- 一個if/then結構可以包含嵌套的比較操作和條件判斷操作
什么是真
#!/bin/bash# 小技巧: # 如果你不能夠確定一個特定的條件該如何進行判斷, #+ 那么就使用if-test結構. echoecho "Testing \"0\"" if [ 0 ] # zero thenecho "0 is true." elseecho "0 is false." fi # 0 為真. echoecho "Testing \"1\"" if [ 1 ] # one thenecho "1 is true." elseecho "1 is false." fi # 1 為真. echoecho "Testing \"-1\"" if [ -1 ] # 負1 thenecho "-1 is true." elseecho "-1 is false." fi # -1 為真. echoecho "Testing \"NULL\"" if [ ] # NULL (空狀態) thenecho "NULL is true." elseecho "NULL is false." fi # NULL 為假. echoecho "Testing \"xyz\"" if [ xyz ] # 字符串 thenecho "Random string is true." elseecho "Random string is false." fi # 隨便的一串字符為真.echoecho "Testing \"\$xyz\"" if [ $xyz ] # 判斷$xyz是否為null, 但是...# 這只是一個未初始化的變量. thenecho "Uninitialized variable is true." elseecho "Uninitialized variable is false." fi # 未定義的初始化為假.echoecho "Testing \"-n \$xyz\"" if [ -n "$xyz" ] # 更加正規的條件檢查. thenecho "Uninitialized variable is true." elseecho "Uninitialized variable is false." fi # 未初始化的變量為假.echo xyz= # 初始化了, 但是賦null值.echo "Testing \"-n \$xyz\"" if [ -n "$xyz" ] thenecho "Null variable is true." elseecho "Null variable is false." fi # null變量為假. echo # 什么時候"false"為真?echo "Testing \"false\"" if [ "false" ] # 看起來"false"只不過是一個字符串而已. thenecho "\"false\" is true." #+ 并且條件判斷的結果為真. elseecho "\"false\" is false." fi # "false" 為真.echoecho "Testing \"\$false\"" # 再來一個, 未初始化的變量. if [ "$false" ] thenecho "\"\$false\" is true." elseecho "\"\$false\" is false." fi # "$false" 為假. # 現在, 我們得到了預期的結果. # 如果我們測試以下為初始化的變量"$true"會發生什么呢? echo exit 0執行結果
andrew@andrew:/work/bash/src$ bash if_true.sh Testing "0" 0 is true.Testing "1" 1 is true.Testing "-1" -1 is true.Testing "NULL" NULL is false.Testing "xyz" Random string is true.Testing "$xyz" Uninitialized variable is false.Testing "-n $xyz" Uninitialized variable is false.Testing "-n $xyz" Null variable is false.Testing "false" "false" is true.Testing "$false" "$false" is false.如果if和then在條件判斷的同一行上的話,必須使用分號結束if表達式,if和then都是關鍵字。關鍵字(或者命令)如果作為表達式的開頭,并且如果想在同一行上再寫一個新的表達式的話,那么必須使用分號來結束上一句表達式。
if [ -x "$filename" ]; then
else if 和elif
elif是else if的縮寫形式,作用是在外部的判斷結構中在嵌入一個內部的if/then結構
if [ condition1 ] thencommand1command2command3 elif [ condition2 ] # 與else if一樣 thencommand4command5 elsedefault-command fiif test condition結構與if [ condition ]完全相同。
test命令在Bash中是內建命令, 用來測試文件類型, 或者用來比較字符串. 因此, 在Bash
腳本中, test命令并不會調用外部的 /usr/bin/test 中的test命令, 這是sh-utils工具包中
的一部分. 同樣的, [也并不會調用 /usr/bin/[ , 這是 /usr/bin/test 的符號鏈接.
test、/usr/bin/test, [], 和/usr/bin/[都是等價的命令
#!/bin/bash echo if test -z "$1" thenecho "No command-line arguments." elseecho "First command-line argument is $1." fi echo if /usr/bin/test -z "$1" # 與內建的"test"命令結果相同. thenecho "No command-line arguments." elseecho "First command-line argument is $1." fi echo if [ -z "$1" ] # 與上邊的代碼塊作用相同. # if [ -z "$1" 應該能夠運行, 但是... #+ Bash報錯, 提示缺少關閉條件測試的右中括號. thenecho "No command-line arguments." elseecho "First command-line argument is $1." fi echo if /usr/bin/[ -z "$1" ] # 再來一個, 與上邊的代碼塊作用相同. # if /usr/bin/[ -z "$1" # 能夠工作, 但是還是給出一個錯誤消息. # # 注意: #在版本3.x的Bash中, 這個bug已經被修正了. thenecho "No command-line arguments." elseecho "First command-line argument is $1." fi echo exit 0在[[]]之間的所有的字符都不會發生文件名擴展或者單詞分割,但是會發生參數擴展和命令替換
使用[[]]而不是[],能夠防止腳本中的許多邏輯錯誤,比如, &&,||, < , 和>操作符能夠正常存在于[[]]條件判斷結構中,但是如果出現在[]結構中的話,會報錯。
if后面也不一定得跟test命令或者用于條件判斷的中括號([]或者[[]])
dir=/home/bozoif cd "$dir" 2>/dev/null; then # "2>/dev/null" 會隱藏錯誤信息.echo "Now in $dir." elseecho "Can't change to $dir." fiif command結構將會返回command的退出碼。
在中括號中的條件判斷也不是非if不可,也可以使用列表結構
var1=20 var2=22 [ "$var1" -ne "$var2" ] && echo "$var1 is not equal to $var2"home=/home/bozo [ -d "$home" ] || echo "$home directory does not exist."算術測試使用(())
#!/bin/bash # 算術測試. # (( ... ))結構可以用來計算并測試算術表達式的結果. # 退出狀態將會與[ ... ]結構完全相反! (( 0 )) echo "Exit status of \"(( 0 ))\" is $?."(( 1 ))echo "Exit status of \"(( 1 ))\" is $?." (( 5 > 4 )) echo "Exit status of \"(( 5 > 4 ))\" is $?." (( 5 > 9 )) echo "Exit status of \"(( 5 > 9 ))\" is $?." (( 5 - 5 )) echo "Exit status of \"(( 5 - 5 ))\" is $?." (( 5 / 4 )) echo "Exit status of \"(( 5 / 4 ))\" is $?." (( 1 / 2 )) echo "Exit status of \"(( 1 / 2 ))\" is $?." (( 1 / 0 )) 2>/dev/null # echo "Exit status of \"(( 1 / 0 ))\" is $?." # "2>/dev/null"起了什么作用? # 如果這句被刪除會怎樣? # 嘗試刪除這句, 然后在運行這個腳本. exit 0條件測試操作符
條件成立返回真,調試不成立時返回假
-e
文件存在
-a
文件存在
這個選項的效果與-e相同. 但是它已經被"棄用"了, 并且不鼓勵使用.
-f
表示這個文件是一個 一般 文件(并不是目錄或者設備文件)
-s
文件大小不為零
-d
表示這是一個目錄
-b
表示這是一個塊設備(軟盤, 光驅, 等等.)
-c
表示這是一個字符設備(鍵盤, modem, 聲卡, 等等.)
-p
這個文件是一個管道
-h
這是一個符號鏈接
-L
這是一個符號鏈接
-S
表示這是一個socket
-t
文件(描述符)被關聯到一個終端設備上
這個測試選項一般被用來檢測腳本中的 stdin ( [終端.-t 0 ] )或者 stdout ( [``-t 1 ] )是否來自于一個
-r
文件是否具有可讀權限( 指的是正在運行這個測試命令的用戶是否具有讀權限)
-w
文件是否具有可寫權限(指的是正在運行這個測試命令的用戶是否具有寫權限)
-x
文件是否具有可執行權限(指的是正在運行這個測試命令的用戶是否具有可執行權限)
-g
set-group-id(sgid)標記被設置到文件或目錄上
如果目錄具有 sgid 標記的話, 那么在這個目錄下所創建的文件將屬于擁有這個目錄的用戶組, 而
不必是創建這個文件的用戶組. 這個特性對于在一個工作組中共享目錄非常有用.
-u
set-user-id (suid)標記被設置到文件上,如果一個root用戶所擁有的二進制可執行文件設置了 set-user-id 標記位的話, 那么普通用戶也會以root權限來運行這個文件. 這對于需要訪問系統硬件的執行程序(比如pppd和cdrecord)非
常有用. 如果沒有suid標志的話, 這些二進制執行程序是不能夠被非root用戶調用的.
對于設置了 suid 標志的文件, 在它的權限列中將會以s 表示.
-k
設置 粘貼位
對于"粘貼位"的一般了解, save-text-mode標志是一個文件權限的特殊類型. 如果文件設置了這個標志, 那么這個文件將會被保存到緩存中, 這樣可以提高訪問速度. 粘貼位如果設置在目錄中, 那么它將限制寫權限. 對于設置了粘貼位的文件或目錄, 在它們的權限標記列中將會顯示 t.
drwxrwxrwt 7 root 1024 May 19 21:26 tmp/
如果用戶并不擁有這個設置了粘貼位的目錄, 但是他在這個目錄下具有寫權限, 那么這個用戶只能在這個目錄下刪除自己所擁有的文件. 這將有效的防止用戶在一個公共目錄中不慎覆蓋或者刪除別人的文件. 比如說 /tmp 目錄. (當然, 目錄的所有者或者 root用戶可以隨意刪除或重命名其中的文件.)
-O
判斷你是否是文件的擁有者
-G
文件的group-id是否與你的相同
-N
從文件上一次被讀取到現在為止, 文件是否被修改過
f1 -nt f2
文件 f1 比文件 f2 新
f1 -ot f2
文件 f1 比文件 f2 舊
f1 -ef f2文件 f1 和文件 f2 是相同文件的硬鏈接
!
“非” – 反轉上邊所有測試的結果(如果沒給出條件, 那么返回真).
測試斷掉的鏈接文件
#!/bin/bash # 一個可以測試鏈接斷掉的符號鏈接的文件,并且可以輸出它們指向的文件 # 以便于它們可以把輸出提供給xargs來進行處理 :) # 比如. broken-link.sh /somedir /someotherdir|xargs rm # #下邊的方法, 不管怎么說, 都是一種更好的辦法: # #find "somedir" -type l -print0|\ #xargs -r0 file|\ #grep "broken symbolic"| #sed -e 's/^\|: *broken symbolic.*$/"/g' # #但這不是一個純粹的bash腳本, 最起碼現在不是. #注意: 謹防在/proc文件系統和任何死循環鏈接中使用! ############################################################## #如果沒有參數被傳遞到腳本中, 那么就使用 #當前目錄. 否則就是用傳遞進來的參數作為目錄 #來搜索. #################### [ $# -eq 0 ] && directorys=`pwd` || directorys=$@#編寫函數linkchk用來檢查傳遞進來的目錄或文件是否是鏈接, #并判斷這些文件或目錄是否存在. 然后打印它們所指向的文件. #如果傳遞進來的元素包含子目錄, #那么把子目錄也放到linkcheck函數中處理, 這樣就達到了遞歸的目的. ########## # 如果是軟連接,則輸出,如果是目錄就進行地遞歸 linkchk () {for element in $1/*; do[ -h "$element" -a ! -e "$element" ] && echo \"$element\"[ -d "$element" ] && linkchk $element# 當然, '-h'用來測試符號鏈接, '-d'用來測試目錄.done } # 把每個傳遞到腳本的參數都送到linkchk函數中進行處理, # 檢查是否有可用目錄. 如果沒有, 那么就打印錯誤消息和 # 使用信息. ################ for directory in $directorys; doif [ -d $directory ]then linkchk $directoryelseecho "$directory is not a directory"echo "Usage: $0 dir1 dir2 ..."fidone exit 0 # 創建一個新文件 name andrew@andrew:/work/bash/src$ touch name # 為name創建符號鏈接 andrew@andrew:/work/bash/src$ ln -s name aaa # 刪除name文件, aaa將會變成丟失鏈接文件的符號鏈接 andrew@andrew:/work/bash/src$ rm name # 查看aaa為執行當前目錄下的name的符號鏈接文件 andrew@andrew:/work/bash/src$ ls -l 總用量 44 lrwxrwxrwx 1 andrew andrew 4 2月 1 13:20 aaa -> name -rwxrwxr-x 1 andrew andrew 8656 1月 30 14:46 a.out -rw-rw-r-- 1 andrew andrew 1887 2月 1 13:08 broken_link.sh -rw-rw-r-- 1 andrew andrew 322 1月 29 13:08 echo_unique.sh -rw-rw-r-- 1 andrew andrew 1513 1月 29 15:55 escape_charater.sh -rw-rw-r-- 1 andrew andrew 279 1月 30 13:48 exit_example.sh -rw-rw-r-- 1 andrew andrew 199 2月 1 11:52 if_else_more.sh -rw-rw-r-- 1 andrew andrew 1946 1月 30 21:03 if_true.sh -rw-rw-r-- 1 andrew andrew 337 1月 29 14:02 single_quotation_mark.sh -rw-rw-r-- 1 andrew andrew 864 2月 1 12:00 test.c # 調用腳本清除當前文件夾中,丟失鏈接文件的符號鏈接 andrew@andrew:/work/bash/src$ bash broken_link.sh ./ | xargs rm andrew@andrew:/work/bash/src$ ls -l 總用量 44 -rwxrwxr-x 1 andrew andrew 8656 1月 30 14:46 a.out -rw-rw-r-- 1 andrew andrew 1887 2月 1 13:08 broken_link.sh -rw-rw-r-- 1 andrew andrew 322 1月 29 13:08 echo_unique.sh -rw-rw-r-- 1 andrew andrew 1513 1月 29 15:55 escape_charater.sh -rw-rw-r-- 1 andrew andrew 279 1月 30 13:48 exit_example.sh -rw-rw-r-- 1 andrew andrew 199 2月 1 11:52 if_else_more.sh -rw-rw-r-- 1 andrew andrew 1946 1月 30 21:03 if_true.sh -rw-rw-r-- 1 andrew andrew 337 1月 29 14:02 single_quotation_mark.sh -rw-rw-r-- 1 andrew andrew 864 2月 1 12:00 test.c- 在將suid標記設置到二進制可執行文件的時候, 一定要小心. 因為這可能會引發安全漏洞.
但是suid標記不會影響shell腳本. - 在當代UNIX系統中, 文件中已經不使用粘貼位了, 粘貼位只使用在目錄中.
其他比較操作符
二元比較操作符,用來比較兩個變量或者數字。
整數比較
-eq
等于
if [ "$a" -eq "$b" ]
-ne
不等于
if [ "$a" -ne "$b" ]
-gt
大于
if [ “a"?gt"a" -gt "a"?gt"b” ]
-ge
大于等于
if [ "$a" -ge "$b" ]
-lt
小于
if [ "$a" -lt "$b" ]
-le
小于等于
if [ "$a" -le "$b" ]
<
小于(在雙括號中使用)
(("$a" < "$b"))
<=
小于等于(在雙括號中使用)
(("$a" <= "$b"))
>
大于(在雙括號中使用)
(("$a" > "$b"))
>=
大于等于(在雙括號中使用)
下一頁
(("$a" >= "$b"))
字符串比較
=
等于
if [ "$a" = "$b" ]
==
等于
if [ "$a" == "$b" ]
與=等價.
==比較操作符在雙中括號對和單中括號對中的行為是不同的.
!=
不等號
if [ "$a" != "$b" ]
這個操作符將在[[ … ]]結構中使用模式匹配.
<
小于, 按照ASCII字符進行排序
if [[ "$a" < "$b" ]]
if [ "$a" \< "$b" ]
注意"<"使用在 [ ] 結構中的時候需要被轉義.
>
大于, 按照ASCII字符進行排序
if [[ "$a" > "$b" ]]
if [ "$a" \> "$b" ]
注意">"使用在 [ ] 結構中的時候需要被轉義.
-z
字符串為"null", 意思就是字符串長度為零
-n
字符串不為"null".
檢查字符串是否為NULL
該腳本只在ubuntu 16.04上測試過,在其他系統上使用記得先使用shellcheck對腳本進行語法檢查
#!/bin/bash # str-test.sh: 檢查null字符串和未引用的字符串, #+ but not strings and sealing wax, not to mention cabbages and kings . . . #+ 但不是字符串和封蠟, 也并沒有提到卷心菜和國王. . . ??? (沒看懂, rojy bug)# 使用 if [ ... ]# 如果字符串并沒有被初始化, 那么它里面的值未定義. # 這種狀態被稱為"null" (注意這與零值不同).if [ -n $string1 ] # $string1 沒有被聲明和初始化.thenecho "String \"string1\" is not null."elseecho "String \"string1\" is null." fi # 錯誤的結果. # 顯示$string1為非null, 雖然這個變量并沒有被初始化.echo# 讓我們再試一下.if [ -n "$string1" ] # 這次$string1被引號擴起來了. thenecho "String \"string1\" is not null." elseecho "String \"string1\" is null." fi # 注意一定要將引用的字符放到中括號結構中!33 echoif [ $string1 ] # 這次, 就一個$string1, 什么都不加. thenecho "String \"string1\" is not null." elseecho "String \"string1\" is null." fi # 這種情況運行的非常好. # [ ] 測試操作符能夠獨立檢查string是否為null. # 然而, 使用("$string1")是一種非常好的習慣. # # 就像Stephane Chazelas所指出的, #if [ $string1 ]只有一個參數, "]" #if [ "$string1" ] 有兩個參數, 一個是空的"$string1", 另一個是"]"echo string1=initializedif [ $string1 ] # 再來, 還是只有$string1, 什么都不加. thenecho "String \"string1\" is not null." elseecho "String \"string1\" is null." fi # 再來試一下, 給出了正確的結果. # 再強調一下, 使用引用的("$string1")還是更好一些, 原因我們上邊已經說過了.string1="a = b"if [ $string1 ] # 再來, 還是只有$string1, 什么都不加. thenecho "String \"string1\" is not null." elseecho "String \"string1\" is null." fi # 未引用的"$string1", 這回給出了錯誤的結果!exit 0執行結果
andrew@andrew:/work/bash/src$ bash str_test.sh String "string1" is not null.String "string1" is null.String "string1" is null.String "string1" is not null. String "string1" is null. #!/bin/bash # zmore#使用'more'來查看gzip文件NOARGS=65 NOTFOUND=66 NOTGZIP=67if [ $# -eq 0 ] # 與if [ -z "$1" ]效果相同 # (譯者注: 上邊這句注釋有問題), $1是可以存在的, 可以為空, 如: zmore "" arg2 arg3thenecho "Usage: `basename $0` filename" >&2 # 錯誤消息輸出到stderr.exit $NOARGS # 返回65作為腳本的退出狀態的值(錯誤碼). fifilename=$1if [ ! -f "$filename" ] # 將$filename引用起來, 這樣允許其中包含空白字符. thenecho "File $filename not found!" >&2 # 錯誤消息輸出到stderr. exit $NOTFOUND fiif [ ${filename##*.} != "gz" ] # 在變量替換中使用中括號結構. then31 echo "File $1 is not a gzipped file!"exit $NOTGZIP fizcat $1 | more# 使用過濾命令'more.' # 當然, 如果你愿意, 也可以使用'less'. exit $? # 腳本將把管道的退出狀態作為返回值. # 事實上, 也不一定非要加上"exit $?", 因為在任何情況下, # 腳本都會將最后一條命令的退出狀態作為返回值.-a
邏輯與exp1 -a exp2, 如果表達式exp1和exp2都為真的話, 那么結果為真.
-o
邏輯或exp1 -o exp2, 如果表達式exp1和exp2中至少有一個為真的話, 那么結果為真.
嵌套的if/then條件測試
可以通過if/then結構來使用嵌套的條件測試,最終的結果和上面使用的&&混合比較操作符的結果是相同的。
if [ condition1 ] thenif [ condition2 ]thendo-something # But only if both "condition1" and "condition2" valid.fi fi檢測你對測試知識的掌握
if [ -f $HOME/.Xclients ]; thenexec $HOME/.Xclients elif [ -f /etc/X11/xinit/Xclients ]; thenexec /etc/X11/xinit/Xclients else# 失敗后的安全設置. 雖然我們永遠都不會走到這來.# (我們在Xclients中也提供了相同的機制) 保證它不會被破壞.xclock -geometry 100x100-5+5 &xterm -geometry 80x50-50+150 &if [ -f /usr/bin/netscape -a -f /usr/share/doc/HTML/index.html ]; thennetscape /usr/share/doc/HTML/index.html & fi fi總結
以上是生活随笔為你收集整理的bash-shell高级编程--条件判断的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于密度的停留点识别方法
- 下一篇: bash-shell--高级编程