天天看點

#yyds幹貨盤點# Bash腳本---條件判斷

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)檔案判斷

以下表達式用來判斷檔案狀态。

  • ​[ -a file ]​

    ​​:如果 file 存在,則為​

    ​true​

    ​。
  • ​[ -b file ]​

    ​​:如果 file 存在并且是一個塊(裝置)檔案,則為​

    ​true​

  • ​[ -c file ]​

    ​​:如果 file 存在并且是一個字元(裝置)檔案,則為​

    ​true​

  • ​[ -d file ]​

    ​​:如果 file 存在并且是一個目錄,則為​

    ​true​

  • ​[ -e file ]​

    ​true​

  • ​[ -f file ]​

    ​​:如果 file 存在并且是一個普通檔案,則為​

    ​true​

  • ​[ -g file ]​

    ​​:如果 file 存在并且設定了組 ID,則為​

    ​true​

  • ​[ -G file ]​

    ​​:如果 file 存在并且屬于有效的組 ID,則為​

    ​true​

  • ​[ -h file ]​

    ​​:如果 file 存在并且是符号連結,則為​

    ​true​

  • ​[ -k file ]​

    ​​:如果 file 存在并且設定了它的“sticky bit”,則為​

    ​true​

  • ​[ -L file ]​

    ​​:如果 file 存在并且是一個符号連結,則為​

    ​true​

  • ​[ -N file ]​

    ​​:如果 file 存在并且自上次讀取後已被修改,則為​

    ​true​

  • ​[ -O file ]​

    ​​:如果 file 存在并且屬于有效的使用者 ID,則為​

    ​true​

  • ​[ -p file ]​

    ​​:如果 file 存在并且是一個命名管道,則為​

    ​true​

  • ​[ -r file ]​

    ​​:如果 file 存在并且可讀(目前使用者有可讀權限),則為​

    ​true​

  • ​[ -s file ]​

    ​​:如果 file 存在且其長度大于零,則為​

    ​true​

  • ​[ -S file ]​

    ​​:如果 file 存在且是一個網絡 socket,則為​

    ​true​

  • ​[ -t fd ]​

    ​​:如果 fd 是一個檔案描述符,并且重定向到終端,則為​

    ​true​

    ​。 這可以用來判斷是否重定向了标準輸入/輸出/錯誤。
  • ​[ -u file ]​

    ​​:如果 file 存在并且設定了 setuid 位,則為​

    ​true​

  • ​[ -w file ]​

    ​​:如果 file 存在并且可寫(目前使用者擁有可寫權限),則為​

    ​true​

  • ​[ -x file ]​

    ​​:如果 file 存在并且可執行(有效使用者有執行/搜尋權限),則為​

    ​true​

  • ​[ file1 -nt file2 ]​

    ​​:如果 FILE1 比 FILE2 的更新時間最近,或者 FILE1 存在而 FILE2 不存在,則為​

    ​true​

  • ​[ file1 -ot file2 ]​

    ​​:如果 FILE1 比 FILE2 的更新時間更舊,或者 FILE2 存在而 FILE1 不存在,則為​

    ​true​

  • ​[ FILE1 -ef FILE2 ]​

    ​​:如果 FILE1 和 FILE2 引用相同的裝置和 inode 編号,則為​

    ​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 ]​

    ​​:如果​

    ​string​

    ​不為空(長度大于0),則判斷為真。
  • ​[ -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.