1、if 結構
if
是最常用的條件判斷結構,隻有符合給定條件時,才會執行指定的指令。
它的文法如下:
if commands; then
commands
[elif commands; then
commands...]
[else
commands]
fi
這個指令分成三個部分:
if
、
elif
和
else
。其中,後兩個部分是可選的。
if
關鍵字後面是主要的判斷條件,
elif
用來添加在主條件不成立時的其他判斷條件,
else
則是所有條件都不成立時要執行的部分。
if
和
then
寫在同一行時,需要分号分隔。
- 分号是 Bash 的指令分隔符。它們也可以寫成兩行,這時不需要分号。
if true
then
echo 'hello world'
fi
if false
then
echo 'it is false' # 本行不會執行
fi
除了多行的寫法,
if
結構也可以寫成單行。
$ if true; then echo 'hello world'; fi
hello world
$ if false; then echo "It's true."; fi
注意:
if
關鍵字後面也可以是一條指令,該條指令執行成功(傳回值
0
),就意味着判斷條件成立。
$ if echo 'hi'; then echo 'hello world'; fi
hi
hello world
if
後面可以跟任意數量的指令。這時,所有指令都會執行,但是判斷真僞隻看最後一個指令,即使前面所有指令都失敗,隻要最後一個指令傳回
0
,就會執行
then
的部分。
$ if false; true; then echo 'hello world'; fi
hello world
上面例子中,
if
後面有兩條指令(
false;true;
),第二條指令(
true
)決定了
then
的部分是否會執行。
2、判斷表達式
if
關鍵字後面,跟的是一個指令。指令的傳回值為
0
表示判斷成立,否則表示不成立。因為這些指令主要是為了得到傳回值,是以可以視為表達式。
常用的判斷表達式有下面這些。
1)檔案判斷
以下表達式用來判斷檔案狀态。
-
:如果 file 存在,則為[ -a file ]
。true
-
:如果 file 存在并且是一個塊(裝置)檔案,則為[ -b file ]
true
-
:如果 file 存在并且是一個字元(裝置)檔案,則為[ -c file ]
true
-
:如果 file 存在并且是一個目錄,則為[ -d file ]
true
-
[ -e file ]
true
-
:如果 file 存在并且是一個普通檔案,則為[ -f file ]
true
-
:如果 file 存在并且設定了組 ID,則為[ -g file ]
true
-
:如果 file 存在并且屬于有效的組 ID,則為[ -G file ]
true
-
:如果 file 存在并且是符号連結,則為[ -h file ]
true
-
:如果 file 存在并且設定了它的“sticky bit”,則為[ -k file ]
true
-
:如果 file 存在并且是一個符号連結,則為[ -L file ]
true
-
:如果 file 存在并且自上次讀取後已被修改,則為[ -N file ]
true
-
:如果 file 存在并且屬于有效的使用者 ID,則為[ -O file ]
true
-
:如果 file 存在并且是一個命名管道,則為[ -p file ]
true
-
:如果 file 存在并且可讀(目前使用者有可讀權限),則為[ -r file ]
true
-
:如果 file 存在且其長度大于零,則為[ -s file ]
true
-
:如果 file 存在且是一個網絡 socket,則為[ -S file ]
true
-
:如果 fd 是一個檔案描述符,并且重定向到終端,則為[ -t fd ]
。 這可以用來判斷是否重定向了标準輸入/輸出/錯誤。true
-
:如果 file 存在并且設定了 setuid 位,則為[ -u file ]
true
-
:如果 file 存在并且可寫(目前使用者擁有可寫權限),則為[ -w file ]
true
-
:如果 file 存在并且可執行(有效使用者有執行/搜尋權限),則為[ -x file ]
true
-
:如果 FILE1 比 FILE2 的更新時間最近,或者 FILE1 存在而 FILE2 不存在,則為[ file1 -nt file2 ]
true
-
:如果 FILE1 比 FILE2 的更新時間更舊,或者 FILE2 存在而 FILE1 不存在,則為[ file1 -ot file2 ]
true
-
:如果 FILE1 和 FILE2 引用相同的裝置和 inode 編号,則為[ FILE1 -ef FILE2 ]
true
下面是一個示例。
#!/bin/bash
FILE=~/.bashrc
if [ -e "$FILE" ]; then
if [ -f "$FILE" ]; then
echo "$FILE is a regular file."
fi
if [ -d "$FILE" ]; then
echo "$FILE is a directory."
fi
if [ -r "$FILE" ]; then
echo "$FILE is readable."
fi
if [ -w "$FILE" ]; then
echo "$FILE is writable."
fi
if [ -x "$FILE" ]; then
echo "$FILE is executable/searchable."
fi
else
echo "$FILE does not exist"
exit 1
fi
上面代碼中,
$FILE
要放在雙引号之中。這樣可以防止
$FILE
為空,因為這時
[ -e ]
會判斷為真。而放在雙引号之中,傳回的就總是一個空字元串,
[ -e "" ]
會判斷為僞。
2)字元串判斷
以下表達式用來判斷字元串。
-
:如果[ string ]
不為空(長度大于0),則判斷為真。string
-
:如果字元串[ -n string ]
的長度大于零,則判斷為真。string
-
[ -z string ]
的長度為零,則判斷為真。string
-
[ string1 = string2 ]
string1
相同,則判斷為真。string2
-
等同于[ string1 == string2 ]
[ string1 = string2 ]
-
[ string1 != string2 ]
string1
不相同,則判斷為真。string2
-
:如果按照字典順序[ string1 '>' string2 ]
排列在string1
之後,則判斷為真。string2
-
[ string1 '<' string2 ]
string1
之前,則判斷為真。string2
注意,指令内部的
>
<
,必須用引号引起來(或者是用反斜杠轉義)。否則,它們會被 shell 解釋為重定向操作符。
#!/bin/bash
ANSWER=maybe
if [ -z "$ANSWER" ]; then
echo "There is no answer." >&2
exit 1
fi
if [ "$ANSWER" = "yes" ]; then
echo "The answer is YES."
elif [ "$ANSWER" = "no" ]; then
echo "The answer is NO."
elif [ "$ANSWER" = "maybe" ]; then
echo "The answer is MAYBE."
else
echo "The answer is UNKNOWN."
fi
上面代碼中,首先确定
$ANSWER
字元串是否為空。如果為空,就終止腳本,并把退出狀态設為
1
。如果
$ANSWER
字元串不為空,就判斷它的值是否等于
yes
no
或者
maybe
- 注意:這裡的
指令把錯誤資訊echo
重定向到标準錯誤,這是處理錯誤資訊的常用方法。There is no answer.
- 注意:字元串判斷時,變量要放在雙引号之中,比如
,否則變量替換成字元串以後,指令可能會報錯,提示參數過多。另外,如果不放在雙引号之中,變量為空時,指令會變成[ -n "$COUNT" ]
,這時會判斷為真。如果放在雙引号之中,[ -n ]
就判斷為僞。[ -n "" ]
3)整數判斷
下面的表達式用于判斷整數。
-
[ integer1 -eq integer2 ]
等于integer1
,則為integer2
true
-
[ integer1 -ne integer2 ]
不等于integer1
integer2
true
-
[ integer1 -le integer2 ]
小于或等于integer1
integer2
true
-
[ integer1 -lt integer2 ]
小于integer1
integer2
true
-
[ integer1 -ge integer2 ]
大于或等于integer1
integer2
true
-
[ integer1 -gt integer2 ]
大于integer1
integer2
true
下面是一個用法的例子。
#!/bin/bash
INT=-5
if [ -z "$INT" ]; then
echo "INT is empty." >&2
exit 1
fi
if [ $INT -eq 0 ]; then
echo "INT is zero."
else
if [ $INT -lt 0 ]; then
echo "INT is negative."
else
echo "INT is positive."
fi
if [ $((INT % 2)) -eq 0 ]; then
echo "INT is even."
else
echo "INT is odd."
fi
fi
上面例子中,先判斷變量
$INT
是否為空,然後判斷是否為
0
,接着判斷正負,最後通過求餘數判斷奇偶。
4)正則判斷
下面的表達式用于正則判斷。
[[ expression ]]
這種判斷形式,支援正規表達式。
[[ string1 =~ regex ]]
上面的文法中,
regex
是一個正則表示式,
=~
是正則比較運算符。
下面是一個例子。
#!/bin/bash
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
echo "INT is an integer."
exit 0
else
echo "INT is not an integer." >&2
exit 1
fi
上面代碼中,先判斷變量
INT
的字元串形式,是否滿足
^-?[0-9]+$
的正則模式,如果滿足就表明它是一個整數。
5)判斷的邏輯運算
通過邏輯運算,可以把多個判斷表達式結合起來,創造更複雜的判斷。
三種邏輯運算
AND
,
OR
,和
NOT
,都有自己的專用符号。
-
運算:符号AND
,也可使用參數&&
-a
-
OR
||
-o
-
NOT
!
下面是一個
AND
的例子,判斷整數是否在某個範圍之内。
#!/bin/bash
MIN_VAL=1
MAX_VAL=100
INT=50
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if [[ $INT -ge $MIN_VAL && $INT -le $MAX_VAL ]]; then
echo "$INT is within $MIN_VAL to $MAX_VAL."
else
echo "$INT is out of range."
fi
else
echo "INT is not an integer." >&2
exit 1
fi
&&
用來連接配接兩個判斷條件:大于等于
$MIN_VAL
,并且小于等于
$MAX_VAL
- 注意:使用否定操作符
時,最好用圓括号确定轉義的範圍。!
if [ ! \( $INT -ge $MIN_VAL -a $INT -le $MAX_VAL \) ]; then
echo "$INT is outside $MIN_VAL to $MAX_VAL."
else
echo "$INT is in range."
fi
上面例子中,指令内部使用的圓括号,必須使用引号或者轉義,否則會被 Bash 解釋。
6)算術判斷
Bash 還提供了
((...))
作為算術條件,進行算術運算的判斷。
if ((3 > 2)); then
echo "true"
fi
上面代碼執行後,會列印出
true
注意,算術判斷不需要使用
test
指令,而是直接使用
((...))
結構。這個結構的傳回值,決定了判斷的真僞。
如果算術計算的結果是非零值,則表示判斷成立。這一點跟指令的傳回值正好相反,需要小心。
$ if ((1)); then echo "It is true."; fi
It is true.
$ if ((0)); then echo "It is true."; else echo "it is false."; fi
It is false.
((1))
表示判斷成立,
((0))
表示判斷不成立。
算術條件
((...))
也可以用于變量指派。
$ if (( foo = 5 ));then echo "foo is $foo"; fi
foo is 5
(( foo = 5 ))
完成了兩件事情。首先把
5
指派給變量
foo
,然後根據傳回值
5
,判斷條件為真。
注意,指派語句傳回等号右邊的值,如果傳回的是
0
,則判斷為假。
$ if (( foo = 0 ));then echo "It is true.";else echo "It is false."; fi
It is false.
下面是用算術條件改寫的數值判斷腳本。
#!/bin/bash
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if ((INT == 0)); then
echo "INT is zero."
else
if ((INT < 0)); then
echo "INT is negative."
else
echo "INT is positive."
fi
if (( ((INT % 2)) == 0)); then
echo "INT is even."
else
echo "INT is odd."
fi
fi
else
echo "INT is not an integer." >&2
exit 1
fi
隻要是算術表達式,都能用于
((...))
文法,詳見《Bash 的算術運算》一章。
7)普通指令的邏輯運算
如果
if
結構使用的不是
test
指令,而是普通指令,比如上一節的
((...))
算術運算,或者
test
指令與普通指令混用,那麼可以使用 Bash 的指令控制操作符
&&
(AND)和
||
(OR),進行多個指令的邏輯運算。
$ command1 && command2
$ command1 || command2
對于
&&
操作符,先執行
command1
,隻有
command1
執行成功後, 才會執行
command2
。對于
||
command1
command1
執行失敗後, 才會執行
command2
$ mkdir temp && cd temp
上面的指令會建立一個名為
temp
的目錄,執行成功後,才會執行第二個指令,進入這個目錄。
$ [ -d temp ] || mkdir temp
上面的指令會測試目錄
temp
是否存在,如果不存在,就會執行第二個指令,建立這個目錄。這種寫法非常有助于在腳本中處理錯誤。
[ ! -d temp ] && exit 1
上面的指令中,如果
temp
子目錄不存在,腳本會終止,并且傳回值為
1
下面就是
if
與
&&
結合使用的寫法。
if [ condition ] && [ condition ]; then
command
fi
#! /bin/bash
filename=$1
word1=$2
word2=$3
if grep $word1 $filename && grep $word2 $filename
then
echo "$word1 and $word2 are both in $filename."
fi
上面的例子隻有在指定檔案裡面,同時存在搜尋詞
word1
word2
,就會執行
if
的指令部分。
下面的示例示範如何将一個
&&
判斷表達式,改寫成對應的
if
結構。
[[ -d "$dir_name" ]] && cd "$dir_name" && rm *
# 等同于
if [[ ! -d "$dir_name" ]]; then
echo "No such directory: '$dir_name'" >&2
exit 1
fi
if ! cd "$dir_name"; then
echo "Cannot cd to '$dir_name'" >&2
exit 1
fi
if ! rm *; then
echo "File deletion failed. Check results" >&2
exit 1
fi
3、case 結構
case
結構用于多值判斷,可以為每個值指定對應的指令,跟包含多個
elif
的
if
結構等價,但是語義更好。它的文法如下。
case expression in
pattern )
commands ;;
pattern )
commands ;;
...
esac
expression
是一個表達式,
pattern
是表達式的值或者一個模式,可以有多條,用來比對多個值,每條以兩個分号(
;
)結尾。
#!/bin/bash
echo -n "輸入一個1到3之間的數字(包含兩端)> "
read character
case $character in
1 ) echo 1
;;
2 ) echo 2
;;
3 ) echo 3
;;
* ) echo 輸入不符合要求
esac
上面例子中,最後一條比對語句的模式是
*
,這個通配符可以比對其他字元和沒有輸入字元的情況,類似
if
else
部分。
下面是另一個例子。
#!/bin/bash
OS=$(uname -s)
case "$OS" in
FreeBSD) echo "This is FreeBSD" ;;
Darwin) echo "This is Mac OSX" ;;
AIX) echo "This is AIX" ;;
Minix) echo "This is Minix" ;;
Linux) echo "This is Linux" ;;
*) echo "Failed to identify this OS" ;;
esac
上面的例子判斷目前是什麼作業系統。
case
的比對模式可以使用各種通配符,下面是一些例子。
-
:比對a)
a
-
a|b)
或a
b
-
:比對單個字母。[[:alpha:]])
-
:比對3個字元的單詞。???)
-
*.txt)
結尾。.txt
-
:比對任意輸入,通過作為*)
結構的最後一個模式。case
#!/bin/bash
echo -n "輸入一個字母或數字 > "
read character
case $character in
[[:lower:]] | [[:upper:]] ) echo "輸入了字母 $character"
;;
[0-9] ) echo "輸入了數字 $character"
;;
* ) echo "輸入不符合要求"
esac
上面例子中,使用通配符
[[:lower:]] | [[:upper:]]
比對字母,
[0-9]
比對數字。
#!/bin/bash
# test.sh
read -n 1 -p "Type a character > "
echo
case $REPLY in
[[:upper:]]) echo "'$REPLY' is upper case." ;;&
[[:lower:]]) echo "'$REPLY' is lower case." ;;&
[[:alpha:]]) echo "'$REPLY' is alphabetic." ;;&
[[:digit:]]) echo "'$REPLY' is a digit." ;;&
[[:graph:]]) echo "'$REPLY' is a visible character." ;;&
[[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;&
[[:space:]]) echo "'$REPLY' is a whitespace character." ;;&
[[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;&
esac
$ test.sh
Type a character > a
'a' is lower case.
'a' is alphabetic.
'a' is a visible character.
'a' is a hexadecimal digit.