1. 程式人生 > >linux shell條件與迴圈舉例

linux shell條件與迴圈舉例

1. if/else 語句

語法:

if condition; then

  commands;
elif condition; then
  commands;
else
  commands;
fi

示例:
需求:指令碼需要1個引數才能正確執行,而在指令碼執行時,如果指定的引數個數不等於1,則shell指令碼就應該打印出一個錯誤資訊,告知使用者指定的引數個數不對,然後結束指令碼的執行。
#!/bin/bash
if [ $# -ne 1 ];then
  echo "Error! Arguments are not correct."
  echo "Usage:$0 parameter"
  exit 1


fi
echo "My shell script beging running."

注意:
1)如果then 和if[ $# -ne 1 ]寫在同一行,then的前面需要加分號
2)elif不能寫成else if.
3)如果使用elif語句,elif後面也要有then
4)不要忘記if語句的結束標誌fi
5)內建測試命令[ ]方括號的兩側都要有空格。

 

2. case語句
case語句的語法格式為:
case word in
  pattern1 )
    command-list1
    ;;
  pattern2 )
    command-list2
    ;;
  pattern3 | pattern4 )


    command-list3
    ;;
  ...
esac

需求:
雖然使用前面介紹的if/elif/else語句可以實現多分支結構,但是當分支較多時,巢狀多個elif語句還是比較麻煩的
解決方案:除了if/else語句以外,還可以使用case語句實現多分支結構
#!/bin/bash
#首先清除螢幕
clear
#顯示帶單項,從而使使用者可以選擇期望的操作
echo " File Operration List"
echo " ----- --------- ----"
echo "Choose one of the following operations:"
echo
echo "[O]pen File"


echo "[D]elete File"
echo "[R]ename File"
echo "[M]ove File"
echo "[C]opy File"
echo "[P]aste File"
echo

#等待使用者的輸入
echo -n "Please enter your operation:"
read operation
#根據使用者輸入的操作執行相應的操作
case "$operation" in
#同時接受大、小寫字母
  "O"|"o")
    #使用者選擇了開啟檔案的操作
    echo "Opening File..."
    ;;
  "D"|"d")
    #使用者選擇了刪除檔案的操作
    echo "Deleteing File..."
    ;;
  "R"|"r")
    #使用者選擇了重新命名檔案的操作
    echo "Rename File1 to File2"
    ;;
  "M"|"m")
    #使用者選擇了移動檔案的操作
    echo "Moving File1 to File2"
    ;;
  "C"|"c")
    #使用者選擇了複製檔案的操作
    echo "Copying File..."
    ;;
  "P"|"p")
    #使用者選擇了貼上檔案的操作
    echo "Paste File..."
    ;;
  *)
    #使用者的輸入不匹配上面的任何一種操作
    #因此是未知操作,以錯誤狀態1退出指令碼
    echo "Error,Unknown operation."
    echo "Progam exit!"
    exit 1
    ;;
esac
exit 0

 

3. while迴圈
while 迴圈的標準語法格式為:
while test-commands;do consequent-commmands;done
只要while迴圈測試的條件為真,他就會不停的執行迴圈體中的程式碼。

需求:

假設想測試一下192.168.1.30到192.168.1.129網段內的所有機器的網路是否可以通訊,這時在指令碼中需要重複執行某一部分的程式碼,或者在滿足某些條件的情況下一直不停的執行迴圈體中的程式碼,直到條件不滿足為止。

可以用while迴圈的條件測試功能來測試條件是否滿足。
#!/bin/bash
#定義要測試的網路
NETWORK=192.168.1
#定義檢測的起始地址
IP=30

#只要IP地址小於130,迴圈體就會被執行
while [ "$IP" -lt 130 ]
do
  echo -en "Pinging ${NETWORK}.${IP}..."
  #變數NETWORK和IP組合在一起構成IP地址
  #執行命令ping檢測IP地址的連通性,並且不顯示任何輸出
  ping -c 2 ${NETWORK}.${IP} >/dev/null 2>&1

  #根據命令ping的退出狀態值是否等於0,判斷網路是否連通
  if [ $? -eq 0 ];then
    #如果ping的退出狀態等於0,列印OK
    echo "OK"
  else
    echo "Failed"
  fi

  #IP 地址加1
  let IP=$IP+1
done
exit 0

# sh 004.sh
Pinging 192.168.1.30...Failed
Pinging 192.168.1.31...OK
Pinging 192.168.1.32...Failed
Pinging 192.168.1.33...Failed
Pinging 192.168.1.34...OK
Pinging 192.168.1.35...Failed
Pinging 192.168.1.36...Failed
Pinging 192.168.1.37...OK
Pinging 192.168.1.38...Failed
Pinging 192.168.1.39...Failed
Pinging 192.168.1.40...Failed
Pinging 192.168.1.41...OK

重要提示
1)do 標明瞭迴圈體的開始;如果do和while[ ]寫在同一行,則do前面應該有分號。
2)不要丟失迴圈體結束標誌done。
3)while後面的test-commands還可以是由多個命令構成的一個list,由分號分開每一個命令,決定是否退出迴圈體的是最後一個命令的返回值。

 

4. until迴圈
需求:迴圈是在測試條件不滿足的情況下執行迴圈體,而當測試條件滿足以後就停止並結束迴圈。
解決方案
當然只要對測試條件進行修改,我們就還是可以使用while迴圈來實現這樣的需求。不過bash為我們提供了更合適這種情況的語句:until語句
#!/bin/bash
QUIT_COMMAND=quit
#直到使用者輸入的字串是quit時,until迴圈才會退出
until [ "$USER_INPUT" = "$QUIT_COMMAND" ] #=左右側須空格
do
  echo "Please input command:"
  echo "(type command $QUIT_COMMAND to exit)"
  read USER_INPUT
  echo
  echo ">>your command is $USER_INPUT"

  #對使用者的輸入進行匹配
  case $USER_INPUT in
    "open")
      echo ">>opening..."
      ;;
    "quit")
      echo ">>bye."
      ;;
    *)
      echo ">>Unknown command."
      ;;
  esac
done
exit 0


在bash手冊中,until的語法和while語句的語法一樣,都是在迴圈的前面先測試一個條件,以此來決定是否要執行後面的迴圈體
語法:
until test-commands;do consequent-commands;done
如果把do和條件測試分別放到不同行,則可以省略分號:

語法
until test-commands
do
  consequent-commands
done

上面的判斷IP是否存活的例子,可以這樣寫
#!/bin/bash
NETWORK=192.168.1
IP=30
#可以寫成until [ ! "$IP" -lt 130 ]
until [ "$IP" -ge "130" ]
do
  echo -en "Testing machine at IP address:${NETWORK}.${IP}..."
  ping -c 2 ${NETWORK}.${IP} >/dev/null 2>&1
  if [ "$?" -eq 0 ];then
    echo "OK"
  else
    echo "Failed"
  fi
  let IP=$IP+1
done
exit 0

重要提示
shell內建的測試命令[ 和]的兩側需要空格
bash中的until條件測試應該寫在迴圈體的上部,和某些語言中的until不同

 

5. for迴圈
需求:
如果我們要對某個目錄下大量的檔案進行相同的操作,重複執行同樣的命令列幾十次甚至上百次是不切實際的,因此也應該用一個自動化指令碼來執行這些相同的操作。
假設當前目錄下有很多副檔名為.sh的指令碼檔案,我們需要把這些.sh檔案都複製到主目錄下的test子目錄中,並且在複製的同時修改檔名,即在每一個檔名的前面都新增字首test_,如何實現:
解決方案
直接使用cp *.sh的方式可以複製所有的.sh檔案,但是不能為檔名新增字首,因此只有在複製一個檔案時,cp命令才能指定目標檔案的名字。在這裡,如果我們能夠遍歷.sh檔案列表中的檔案,就可以單獨對每一個檔案執行復制操作,並在目標檔案的檔名新增字首test_,因此我們所需要的就是一種遍歷.sh檔案列表的能力。前面介紹了while和until迴圈都是根據某一個條件是否滿足來決定迴圈是否繼續執行的,因此在本例中並不合適。對於遍歷檔案列表這種需求,使用for迴圈是最好的選擇:
#!/bin/bash
#在主目錄下建立測試目錄
mkdir ${HOME}/test >/dev/null 2>&1
#如果建立目錄不成功
if [ $? -ne 0 ];then
  echo "Directory ${HOME}/test has already existed."
  echo "Do Nothing,Abort!"
  exit 1
else
  echo "Create directory ${HOME}/test"
fi

echo "cp *.sh files into directory ${HOME}/test,and prefix \"test_\" befor each filename."

#遍歷當前目錄下的所有*.sh檔案
for FILE in `ls *.sh`
do
  #把檔案複製到剛建立目錄下,並重命名為test_*
  cp $FILE ${HOME}/test/test_$FILE
  #為每一個檔案新增可執行許可權
  chmod +x ${HOME}/test/test_$FILE
done
exit 0


語法:
for 迴圈會讓你對一個列表中的每一項都重複的執行相同的命令
for name in word1 word2 ...wordN
do
  commands
done

name是一個變數的名字,擴充套件in後面的words為一個空格分隔的列表,也可以直接寫出這個空格分隔的列表word1到wordN,其中
每一項都是一個字串。每一次迴圈中,變數name都會取列表中的一個字串的值,第一次進入for迴圈時,變數name被設定為word1的值,第二次,變數被設定為word2,以此類推,直到最後一次name等於wordN,然後結束迴圈。

 

6. select迴圈
需求:
如果想快速建立一個有若干個選項的選單,並且每一個選項前面都安裝順序標好號,使用者只要選擇對應的數字標號即可,如何快速實現這一的選單?例如寫一個程式以選單的形式列出一些系統目錄,然後使用者選擇顯示哪個系統目錄下的檔案。
解決方案
bash為我們提供理論一個快速建立選單的工具,就是select迴圈
#!/bin/bash
echo
echo "Which directory do you want to list:"
echo "(Press \"Enter\" directly to show menu again)"
echo
#把要顯示的選單項寫在in後面的列表裡
select dir in /bin/ /usr/bin/ /usr/local/bin/ /sbin/ /usr/sbbin/ /usr/local/sbin/ /usr/share/ quit
do
  if [ ! -z "$dir" ];then
    if [ "$dir" = "quit" ];then
      #如果使用者選擇quit,則退出指令碼
      echo "You entered quit,Byebye!"
      break;
    else
      #使用者選擇了一個目錄
      echo "You selected directory \"$dir\",it contains following files:"
      #顯示所選目錄中的內容
      ls -l $dir
    fi
  else
    echo "Error,invalid,chose agein!"
  fi

  echo
  echo "Which directory do you want to list:"
  echo "(Press \"Enter\" directly to show menu again)"
done


執行結果如下,選擇對應的選項執行對應的行為
# sh 008.sh

Which directory do you want to list:
(Press "Enter" ddirectly to show menu again)

1) /bin/ 4) /sbin/ 7) /usr/share/
2) /usr/bin/ 5) /usr/sbbin/ 8) quit
3) /usr/local/bin/ 6) /usr/local/sbin/
#? 8

You entered quit,Byebye!

使用select結構可以快速生成選擇選單,他的語法結合for語句很類似
select name [in words ...];do commands;done
除了關鍵字由for替換成select以外,其他的幾乎沒變。

select為我們做了很多事情,其執行過程如下:
(1)首先把in後面列表中的每一項都顯示出來,並且在每一項前面新增一個數字的標號
(2)然後顯示一個提示符,等待使用者的輸入,提示符通常是#?,但是可以修改,下面會寫到
(3)使用者輸入數字標號來選擇相應的選項,無論使用者輸入什麼,都會儲存到變數$REPLY中
(4)如果使用者輸入的數字標號可以和一開始顯示出來的選單項的數字標號對應上,則變數name的值被設定為數字標號後面的選項,也就是用空格分隔的列表中的某一個字串,如果不能匹配,則不設定變數name的值
(5)無論使用者的輸入與選單項的數字標號是否能夠匹配,都會執行do和done之間的迴圈體,在這裡需要通過變數name的值來判斷是否成功匹配,從而執行相應的操作;對於使用者什麼都沒有輸入,之間按Enter鍵的情況,不會執行do/done之間的迴圈體,而是與剛開始執行select語句一樣,再次輸出所有的選單項和提示符,等待使用者輸入
(6)迴圈體執行完成以後,再次輸入提示符等待使用者輸入,這也是select迴圈和其他迴圈最不同的地方,它沒有專門用於判斷是否應該退出迴圈的測試條件,因此需要我們在do/done之間的迴圈體內使用迴圈控制語句break來退出迴圈,或者直接使用exit退出shell指令碼的執行。

通常可以把select語句和case語句一起使用,如上面的例子可以通過case語句修改為更簡單的形式
#!/bin/bash
echo
echo "Which directory do you want to list:"
echo "(Press \"Enter\" directly to show menu again)"

#把要顯示的選單項寫在in後面的列表裡
select dir in /bin /usr/bin /usr/local/bin /sbin/usr/sbin /usr/local/sbin quit
do
  case $dir in
    quit)
      #如果使用者選擇quit,則退出指令碼
      echo "You entered quit,Byebye!"
      break
      ;;
    /*)
      #匹配所選擇目錄,$dir以/開始說明使用者選擇了某個目錄
      echo "You selected directory \"$dir\",it contains following files:"
      echo
      #顯示所選目錄中的內容
      ls -l $dir
      ;;
    *)
      #所有其他的輸入都為非法
      echo "Error,invalid selection \"$REPLY\",chose again!"
      ;;
  esac

  echo
  echo "Which directory do you want to list:"
  echo "(Press \"Enter\" directly to show menu again)"
done
exit 0

select 迴圈預設的提示符是#? 如果想修改成自定義的提示符,可以在shell指令碼的開頭一行#!/bin/bash後面加入
PS3=">"
這一行,從而把select迴圈的提示符修改為 > ,由此可知,設定PS3這個變數可以指定selec迴圈的提示符

提示
忘記寫退出select迴圈的語句,會導致迴圈無休止的執行下去

 

7. break語句
break語句的語法格式為:
break [n]
break語句的作用是從一個迴圈結構中退出,這個迴圈結構可以是for迴圈,while迴圈,until迴圈或select迴圈。如果沒有指定引數n,則結束最內層的迴圈;如果指定了可選引數n,則跳出n層迴圈。n的值必須大於等於1.如果指定的引數n大於包圍break語句的迴圈個數,就跳出所有迴圈語句


8. continue語句
用來結束當前迴圈的本次迭代,而繼續迴圈下次迭代。

continue語句的語法和break語句的語法格式一樣,為:
continue [n]
continue語句的作用是:繼續執行包囊continue的for,while,until,或select迴圈語句的下一次迭代。如果指定了引數n,則繼續執行包囊continue語句的第n層的迴圈語句的下一次迭代。引數必須大於等於1.只要n大於等於1,continue語句就返回0

重要提示
continue引數n的含義是:結束當前迴圈迭代,從包囊continue n語句的第n層迴圈繼續執行,開始第n層迴圈的下一次迭代。很容易將其錯誤的理解成跳過最內層迴圈的n次迭代,繼續執行最內層迴圈