unix 下shell編程(二)

3>在Shell中使用數據變量 

  用戶可以在Shell中使用數據變量,例如ba.sh程序: 

    cd/usr/icewalk 
    ls|cpio -o > /dev/fd0 

  該程序中要備份的目錄爲一常量,即該程序只能用來備份一個目錄。若在該程序中使用變量,則會使其更通用: 

    workdir=$1 
    cd $workdir 
    ls * |cpio -o > /dev/fd0 

  通過這一改變,用戶可以使用程序備份變量$workdir指定的目錄。例如我們要備份/home/www的內容,只要運行ba.sh /home/www即可實現。(若不明白 $1,下面將詳細介紹shell參數的傳遞,$1代表本sh程序-ba.sh的第一個參數) 

  4>在Shell程序中加上註釋 

  爲了增加程序的可讀性,我們提倡加入註釋。在Shell程序中註釋將以"#"號開始。當Shell解釋到"#"時,會認爲從"#"號起一直到該行行尾爲註釋。 

  5>對Shell變量進行算術運算 

  高級語言中變量是具有類型的,即變量將被限制爲某一數據類型,如整數或字符類型。Shell變量通常按字符進行存儲,爲了對Shell變量進行算術運算,必須使用expr命令。 

  expr命令將把一個算術表達式作爲參數,通常形式如下: 

    expr [數字] [操作符] [數字] 

  由於Shell是按字符形式存儲變量的,所以用戶必須保證參加算術運算的操作數必須爲數值。下面是有效的算術操作符: 

    +   兩個整數相加 
    -   第一個數減去第二個數 
    *   兩整數相乘 
    /   第一個整數除以第二個整數 
    %   兩整數相除,取餘數 
  例如: 
    $expr 2 + 1 
     結果顯示:3 
    $expr 5 - 3 
     結果顯示:2 

若expr的一個參數是變量,那麼在表達式計算之前用變量值替換變量名。 
    $int=3 
    $expr $int + 4 
    結果顯示:7 
  用戶不能單純使用"*"做乘法,若輸入: 
    $expr 4*5 
  系統將會報錯,因爲Shell看到"*"將會首先進行文件名替換。正確形式爲: 
    $expr 4 \* 5 
     結果顯示:20 
  多個算術表達式可以組合在一起,例如: 
    $expr 5 + 7 / 3 
    結果顯示:7 
  運算次序是先乘除後加減,若要改變運算次序,必須使用"`"號,如: 
    $int=`expr 5 + 7` 
    $expr $int/3 
     結果顯示:4 
    或者: 
    $expr `expr 5+7`/3 
    結果顯示:4 

  6>向Shell程序傳遞參數 

  一個程序可以使用兩種方法獲得輸入數據。一是執行時使用參數。另一種方法是交互式地獲得數據。vi編輯程序可以通過交互式的方法獲得數據,而ls和expr則從參數中取得數據。以上兩種方法Shell程序都可以使用。在"交互式讀入數據"一節中將介紹Shell程序通過交互式的方法獲得參數。 

  通過命令行給Shell程序傳遞參數可以擴大程序的用途。以前面提到的ba.sh程序爲例: 
  $cat >re.sh 
  cd $workdir 
  cpio -i < /dev/fd0 
  ^d 

  程序re.sh恢復了ba.sh程序備份的所有文件。若只從軟盤上恢復一個指定的文件,可以用該文件名作爲參數,傳遞給Shell程序re.sh: 

  程序改寫如下: 
  $cat >re2.sh 
  cd $workdir 
  cpio -i $1 < /dev/fd0 
  ^d 

  用戶可以指定要恢復的文件,例如fname 

  $re2.sh fname 

此時文件fname作爲第一個位置參數傳遞給re2.sh,re2.sh的缺點是要恢復兩個或多個文件要重複運行,我們可以用$*變量傳遞不確定的參數給程序: 

  $cat >re3.sh 
  cd $workdir 
  cpio -i $* < /dev/fd0 
  ^d 

  我們就可以恢復多個文件,例如fname1,fname2,fname3 
  $re3.sh fname1 fname2 fname3 
  (以上程序re.sh,re2.sh,re3.sh,假設用戶已經chmod了可執行權利) 

  因爲沒有賦值的變量可以作爲NULL看待,所以若是程序re3.sh在執行時候沒賦予參數,那麼一個空值將被插入到cpio命令中。該命令將恢復所有保存的文件。 

條件判斷語句 

  條件判斷語句是程序設計語言中十分重要的語句,該語句的含義是當某一條件滿足時,執行指定的一組命令。 

1>if - then語句 

  格式: if command1 
     then 
       command2 
       command3 
     fi      ---(if 語句結束) 
       command4 

  每個程序或命令執行結束後都有一個返回的狀態,用戶可以用Shell變量$?獲得這一狀態。if語句檢查前面命令執行的返回狀態,若該命令成功執行,那麼在then和fi之間的命令都將被執行。在上面的命令序列中,command1和command4總要執行。若command1成功執行,command2和command3也將執行。 

  請看下面程序: 
    #unload -program to backup and remove files 
    cd $1 
    ls -a | cpio -o > /dev/mnt0 
    rm * 

  該程序在備份資料後,刪除檔案,但當cpio命令不能成功執行時,rm命令還是把資料刪除了,我們可不希望這樣,爲了避免此情況,可以用if - then語句: 
    #--卸載和判斷刪除程序 

    cd $1 
    if ls -a | cpio > /dev/mnt0 
    then 
      rm * 
    fi 
  上面程序在cpio執行成功後才刪除檔案 

同時,若執行沒有成功,我們希望得到提示,sh中的echo命令可以向用戶顯示消息,並顯示後換行,上面程序可以寫成: 
     #--卸載和判斷刪除程序 
    cd $1 
    if ls -a | cpio > /dev/mnt0 
    then 
      echo "正刪除文件資料... ..." 
      rm * 
    fi 

  echo命令可以使用一些特殊的逃逸字符進行格式化輸出,下面是這些字符及其含義: 

    \b  Backspace 
    \c  顯示後不換行 
    \f  在終端上屏幕的開始處顯示 
    \n  換行 
    \r  回車 
    \t  製表符 
    \v  垂直製表符 
    \   反斜框 
    \0nnn 用1,2或3位8進制整數表示一個ASCII碼字符 

2>if - then - else語句 

  不用多說它的作用,別的高級語言中都有,格式爲: 
  if command1 
  then 
    command2 
    command3 
  else 
    command4 
    command5 
  fi 

  在此結構中,command1中是先執行,當command1成功執行時,將執行command2和command3,否則執行command4和command5 

  注意看下面程序: 
    #備份程序 
    cd $1 
    if ls -a |cpio -o > /dev/mnt0 
    then 
      echo "刪除源資料... ..." 
      rm * 
    else 
      echo "磁帶備份失敗!" 
    fi 

3>test命令進行條件測試 

  if語句可以通過測試命令執行的返回狀態來控制命令的執行,若要測試其他條件,在bsh中可以使用test命令。該命令檢測某一條件,當條件爲真時返回0,否則返回非0值。test命令可以使Shell程序中的if語句象其他程序語言中的條件判斷語句一樣,具有很強的功能。 

  test命令的使用方法爲: 
    test condition 
  可測試的條件分爲4類: 
  1)測試兩個字符串之間的關係。 
  2)測試兩個整數之間關係。 
  3)測試文件是否存在或是否具有某種狀態或屬性。 
  4)測試多個條件的與(and)或(or)組合。 

1、條件語句>>test語句 

1>測試字符串間的關係 

  bsh把所有的命令行和變量都看作字符串。一些命令如expr和test可以把字符當作數字進行操作。 

  同樣任何數字也可以作爲字符串進行操作。 

  用戶可以比較兩個字符串相等或不等,也可以測試一個串是否賦了值。有關串的操作符如下: 
    str1 = str2      當兩個串有相同內容、長度時爲真 
    str1 != str2      當串str1和str2不等時爲真 
    -n str1         當串的長度大於0時爲真(串非空) 
    -z str1         當串的長度爲0時爲真(空串) 
    str1           當串str1爲非空時爲真 

  不但Shell程序可以使用test進行條件判斷,test命令也可以獨立執行,如: 

    $str1=abcd 
    $test $str1 = abcd 
    $echo $? 
    結果顯示:0 

與上例中第一行賦值語句中的等號不同,test命令中的等號兩邊必須要有空格。本例test命令共有3個參數。注意兩個串相等必須是長度和內容都相等。 

    $str1="abcd " 
    $test "$str1" = abcd 
    $echo $? 
    結果顯示:1 

  上面str1包含5個字符,其中最後一個爲空格符。而test命令中的另一個串只有4個字符,所以兩串不等,test返回1。 

  不帶任何操作符和使用-n操作符測試一個串結果是一樣的,例如: 

    $str1=abce 
    $test $str1 
    $echo $? 
    結果顯示:0     
    $test -n $str1 
    $echo $? 
    結果顯示:0 

  但是,上面兩條命令也有一點差別,反映出了使用test命令潛在的問題,請看下例: 

    $str1="   " 
    $test $str1 
    $echo $? 
    結果顯示:1 
    $test -n "$str1" 
    $echo $? 
    結果顯示:0 
    $test -n $str1 
    結果顯示:test:argument expected 

  上例中,第一次測試爲假因爲Shell在執行命令行之前首先要進行變量替換,即把$str1換成空格,然後shell又將命令行上的空格刪除,故test命令測試到的爲空串。而在第二次測試中,變量替換後空格位於括號內,故不會被刪除,test測試到的是一個包含空格的串,在第三次測試中,shell把空格刪除,只把-n傳個test命令,所以顯示參數錯。 

2>測試兩個整數之間關係 

  test命令與expr命令一樣,也可以把字符轉變成整數,然後對其操作。test命令對兩個數進行比較,使用的操作符如下: 

    int1 -eq int2    兩數相等爲真 
    int1 -ne int2    兩數不等爲真 
    int1 -gt int2    int1大於int2爲真 
    int1 -ge int2    int1大於等於int2爲真 
    int1 -lt int2    int1小於int2爲真 
    int1 -le int2    int1小於等於int2爲真 

  下面的例子反映了字符串比較與數字比較的不同: 

    $str1=1234 
    $str2=01234 
    $test $str1 = $str2 
    $echo $? 
    結果顯示:1 
    $test $str1 -eq $str2 
    $echo $? 
    結果顯示:0 

3>有關文件的測試 

  使用test進行的第三類測試是測試文件的狀態,用戶可以測試文件是否存在,是否可寫以及其他文件屬性。下面是文件測試時使用的選項。注意只有文件存在時,纔有可能爲真。 

  -r file     用戶可讀爲真 
  -w file     用戶可寫爲真 
  -x file     用戶可執行爲真 
  -f file     文件爲正規文件爲真 
  -d file     文件爲目錄爲真 
  -c file     文件爲字符特殊文件爲真 
  -b file     文件爲塊特殊文件爲真 
  -s file     文件大小非0時爲真 
  -t file     當文件描述符(默認爲1)指定的設備爲終端時爲真 
4>複雜的條件測試(and 、or 、not) 
  -a         與 
  -o        或 
  !        非 
  就是組合條件了,任何高級語言中都有的(NOT 、AND 、OR),例如: 
    $test -r em.null -a -s em.null 
    $echo $? 
    結果顯示:1 
    說明了em.null並不是可讀並且非空的文件 

5>另一種執行test的方法 

  bsh中還有另一種執行test命令的方法,就是把測試條件放到一對[ ]中,例如: 
    $int1=4 
    $[ $int1 -gt 2 ] 
    $echo $? 
    結果顯示:0 

要注意在[ 的後面和 ]符號的前面要有一個空格。 
  下面我們用test命令寫個簡單但比較完善的程序: 

    #-- 備份程序 
   
    #-- 檢查參數 
    if [ $# -ne 1 ] 
    then 

      echo "請在程序名後面指出要備份文件所在目錄!" 
      exit 1 
    fi 
    #-- 檢查目錄名是否有效 
    if [ !-d "$1" ] 
    then 
      echo "$1 不是一個目錄!" 
      exit 2 
    fi 
    cd $1 
    ls -a | cpio -o >/dev/mnt0 
    if [ $? -eq 0 ] 
    then 
      rm * 
    else 
      echo "cpio執行不成功!備份失敗..." 
      exit 3 
    fi 

6>空命令 

  在Bsh中用 : 代表空命令,就是充個數,什麼都不做 

7>嵌套if語句和elif結構 

  檢查條件1 
  A:當條件1爲真,則執行一部分操作 
  B:若條件1爲假,檢查條件2 
    1)若條件2爲真,執行另外一部分操作 
    2)若條件2爲假,檢查條件3 
    3)若條件3爲真,執行其他一部分操作 
  語法如下: 
    if command 
    then 
      command 
    else 
      if command 
      then 
        command 
      else 
        if command 
        then 
          command 
        fi 
      fi 
    fi 

8>elif語句 

  嵌套if語句有時會給用戶帶來混亂,特別是什麼時候fi語句很難判斷。因此Bourne Shell又提供了elif語句。elif是else-if的縮寫,它表示是if語句的繼續。格式爲: 

    if command 
    then 
      command 
    elif command 
    then 
      command 
    elif command 
    then 
      command 
    fi 

  上面介紹的嵌套if語句和elif語句完成相同的功能,用戶可以根據自己的喜好選擇一種使用。 

9>case語句 

  前面說的elif語句替代if-then-else語句,但有時在編程時還會遇到對同一變量進行多次的測試,該情況可以用多個elif語句實現,但還有一種更簡單的方法就是用case語句。 

  case語句不但取代了多個elif和then語句,還可以用變量值對多個模式進行匹配,當某個模式與變量值匹配後,其後的一系列命令將被執行,下面是case語句使用的語句。 

  case value in 
   pattem 1) 
    command 
    command;; 
   pattem 2) 
    command 
    command;; 
   .... 
   pattem) 
    command; 
  esac 

  case語句只執行其中的一組命令,當變量值與多個模式相匹配時,只有第一個匹配的模式對應的命令被執行。";;"表示該模式對應的命令部分程序。 

  通過學習下面的read語句,我們們再舉例子說明case語句的用法。 

10>read語句 

  Shell程序不但可以通過命令行參數得到輸入數據,還可以使用read命令提示用戶輸入數據,其語法格式爲: 

  read var1 var2... ...varn 

當Bsh遇到一個read語句時,在標準輸入文件中讀取數據直到一個換行符。此時Shell在解釋輸入行時,不進行文件名或變量的替換,只是簡單地刪除多餘的空格。然後Shell將輸入行的第一個字的內容給變量1,第二個給變量2,直到所有變量都賦上值或是輸入行爲空。若輸入行中字的個數超過變量個數,Shell將把輸入行中剩餘的所有字的內容都賦給最後一個變量。當變量個數多於輸入行字的個數時候,多於的變量將賦一個空值。輸入行的每一個字是由空格分隔的一個字母和數字組成的字符串。 

  $read var1 var2 var3 
    輸入:Hello my friend 
   
  $echo $var1 $var2 $var3 
    結果顯示:Hello my friend 
  $echo $var2 
    結果顯示:my 

下面用個read和case的例子結束本部分的學習: 

  #--交互式備份,恢復程序 
  echo "輸入要備份文件所在目錄:\c" 
  read WORKDIR 
  if [ !-d $WORKDIR ] 
  then 
    echo "Sorry,$WORKDIR is not a directory" 
    exit 1 
  fi 
  cd $WORKDIR 
  echo "輸入選擇:" 
  echo _ 
  echo "1.恢復到 $WORKDIR" 
  echo "2.備份 $WORKDIR" 
  echo "0.退出" 
  echo 
  echo "\c" 
  read CHOICE 
  case "$CHOICE" in 
   1)echo "恢復中... ..." 
    cpio -i < /dev/mnt0;; 
   2)echo "備份中... ..." 
    ls | cpio -o > /dev/mnt0;; 
   0)exit 1 
   *)exit 1 
  esac 
  if [ $? -ne 0 ] 
  then 
   echo "程序運行中出現錯誤!" 
  else 
   echo "操作成功!" 
fi   
  
  在上面代碼中,"*"定義了其他模式下不匹配時的默認操作。 

循環語句 

  前面介紹的程序和所學的語句都是從頭到尾成一條主線下來,或是成分支結構,在日常管理UNIX的過程中,經常要重複的做一些操作,處理批量的問題,這就涉及到了循環結構,同高級語言相似,UNIX的Shell也提供了強大的循環處理語句。 

  Bsh語言中有三種循環語句-while循環、until循環、for循環,下面通過具體的例子分別介紹這三種結構。 

While循環 

  在while循環語句中,當某一條件爲真時,執行指定的命令。語句的結構如下: 

while command 
do 
  command 
  command 
  … … 
done 

示例代碼如下: 

#測試while循環小程序 

x_t=1 
  while [  $x_t -lt 5 ] 
  do 
     mm=` expr $x_t \* $int `  #注意"\"的作用 
     echo "$mm" 
     x_t=` expr $x_t + 1 `   #注意expr的用法 
  done 
  echo "THE WHILE IS END!\n" 

程序的執行結果如下: 



16 
THE WHILE IS END 

  在上述程序中,當變量x_t的值小於5的時候,執行while循環中的語句。在第五次循環時, [ $x_t-lt5]命令返回非零值,於是程序執行done後面的代碼。 
現在利用while循環,可以改進我們早些時候用的備份數據的例子,當用戶指定的目錄備份完畢後,使用while循環使程序執行一次可以備份多個用戶指定的目錄。代碼如下: 

echo "歡迎使用備份小程序" 

  ANS=Y 
  while [ $ANS = Y -o $ANS = y ] 
  do 
    echo _ 
    #讀目錄名 
    echo "輸入要備份的目錄名:\c" 
    read DIR 
    if [ ! -d $DIR ] 
    then 
        echo "$DIR不是一個目錄!" 
        exit 1 
    fi 
    cd $DIR 
    echo "請選擇:" 
    echo _ 
    echo "1 恢復數據到 $DIR" 
    echo "2 備份$DIR的數據" 
    echo 
    echo "請選擇:\c" 
    read CHOICE 
    case "$CHOICE" in 
       1) echo "恢復中… …" 
        cpio -i        2) echo "備份中… …" 
        cpio -o >/dev/rmt0;; 
       *) echo "選擇無效" 
    esac 
    if [ $? -ne 0 ] 
    then 
       echo "cpio執行過程中出現問題" 
       exit 2 
    fi 
    echo "繼續別的目錄嗎?(Y/y)\c" 
    read ANS 
  done 

  在程序開始,我們給變量ANS符值爲Y,根據whlie的判斷條件,程序進入while循環,執行do-done中的語句,每次循環都要求用戶輸入ANS的值用來判斷是否進行下次重複執行do-done中的語句。如果用戶輸入的條件不滿足while語句條件,循環結束,程序執行done後面的語句。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章