天天看點

Linux指令行與Shell腳本程式設計(續)

一、 處理使用者輸入

1) 指令行參數

   1.1) 讀取參數

            bash shell 會将一些稱為位置參數的特殊變量配置設定給指令行輸入的所有參數。位置參數變量是标準的數字:$0是程式名,$1是第一個參數,$2是第二個參數,以此類推,直      到第9個參數$9。

    1.2) 讀取程式名

             你可以用$0參數來擷取shell在指令行啟動的程式的名字。但是有個小問題需要處理一下,例如執行/home/rich/test_bin.sh這個shell檔案時,直接$0傳回的是/home/rich/test_bin.sh這樣一個完整的包含路徑的程式名。有時我們可能隻需要傳回test_bin.sh這樣一個程式名,則可以通過如下方式來擷取:

      programe_name=`basename $0`

    1.3)測試參數

               有時候,你需要測試參數是否存在。例如:

                if [ -n $1 ]

                 then

                            echo "the first parameter exists"

                  else

                              echo "the first parameter doesn't exist"

                 fi

2)  特殊參數變量

          2.1) 參數計數 $#

                    擷取最後一個參數${!#}

            2.2) 抓取所有的資料

                     有些情況下,你隻想抓取指令行上提供的所有參數,然後周遊它們。你可以使用一組其他的特殊變量,而不用先用$#變量來判斷指令行上有多少參數然後再周遊它                 們。$*和[email protected]變量提供了對所有參數的快速通路。這兩個都能夠在單個變量中存儲所有的指令行參數。

                     $*變量會将指令行上提供的所有的參數當做單個單詞儲存。而[email protected]會将其當做多個獨立的單詞。

3) 移動變量

             bash shell工具鍊中的另一個工具是shift指令。bash shell提供了shift指令來幫助操作指令行參數。跟字面上的意思一樣,shift指令會根據它們的相對位置來移動指令行參      數。在使用shift指令時,預設情況下它會将每個參數變量減1。是以$3的值會移動到$2,$2的值會移動到$1。而$1的值則會被删除(注意,$0的值是程式的名字)

           例如:

Linux指令行與Shell腳本程式設計(續)

           另外,你也可以給shift指令提供一個參數來執行多位移動。

4) 處理選項

     選項是跟在單破則号後面的單個字母,能改變指令的行為。

     4.1) 查找選項

              表面上看,指令行選項也沒什麼特殊。在指令行上,它們緊跟在腳本名之後,就跟指令行參數一樣。實際上,如果願意,你可以像處理指令行參數一樣處理指令行

       選項。

            4.1.1) 處理簡單指令行選項

Linux指令行與Shell腳本程式設計(續)

          4.1.2) 分離參數和選項

               你經常會遇到想在shell腳本中同時使用選項和參數的情況。Linux中處理這個問題的标準方式是用特殊字元來将二者分開,該字元會告訴腳本選項何時結束以及普通參

         數何時開始。對于Linux來說,這個特殊字元是雙破折線(--)。shell會用雙破折線來表明選項結束了。看到雙破折線之後,腳本會安全地将剩下的指令當做參數來處理,而

         不是選項。

Linux指令行與Shell腳本程式設計(續)

          4.1.3) 處理帶值得選項

   4.2) 使用getopt指令

          getopt指令是一個在處理指令行選項和參數時非常友善的工具。它能夠識别指令行參數,進而在腳本中解析它們時更友善。

         4.2.1) 指令的格式

                getopt options optstring parameters

                其中,optstring是這個過程的關鍵所在。它定義了指令行有效的選項字母,還定義了哪些選項字母需要參數值。

                首先,在optstring中列出你要在腳本中用到的每個指令行選項字母。然後,在每個需要參數值的選項字母後加一個冒号。getopt會基于你定義的optstring解析提供的參

                數。例如:

                  # getopt ab:cd -a -b test1 -cd test2 test3

                     -a -b test1 -c -d -- test2 test3

                    optstring定義了4個有效選項字母,a、b、c、d。它還定義了選項字母b需要一個參數值。當getopt指令運作時,它會檢查提供的參數清單,并基于提供的optstring解

               析。注意它會自動将-cd選項分成兩個單獨的選項,并插入雙破折線來分開行中的額外參數。

              注意,如果你指定了一個不在optstring中的選項,預設情況下,getopt指令會産生一條錯誤消息。如果要忽略這條錯誤消息,可以在指令後加-q選項。

      4.2.2) 在腳本中使用getopt

             你可以在腳本中使用getopt來格式化輸入給腳本的任何指令行選項。但是用起來略微複雜。方法是用getopt指令生成的格式化後的版本來替換已有的指令行選項和參數。

        這個用set指令可以做到。set指令的選項之一就是雙破折線,它會将指令行參數替換成set指令的指令行的值。使用方式看起來如下:

         set -- `getopt -q ab:cd "[email protected]"`

        例如:

Linux指令行與Shell腳本程式設計(續)

       但是,上面這個程式仍存在一個小問題,例如當執行:./bash_1.sh -a -b test1 -c "test2 test3" test4時,會将"test2 test3"分開來解析。然而,我們可以用下面的方法來解決。

       4.2.3) 使用更進階的getopts

            每次調用它時,它隻處理一個指令行上檢測到的參數。處理完所有的參數後,它會退出并傳回一個大于0的退出狀态碼。getopts指令的格式如下:

            getopts optstring variable

             optstring值類似于getopt指令中的那個。有效的選項字母都會列在optstring中,如果選項字母要求有個參數值,就加一個冒号。如果要去掉錯誤消息的話,可以

       在optstring之前加一個冒号。getopts指令會将目前參數儲存在指令行中定義的variable中。

              getopts指令會用到兩個環境變量。如果選項需要跟一個參數值,OPTARG環境變量就會儲存這個值。OPTIND環境變量儲存了參數清單中getopts正在處理的參數

      位置。這樣你就能在處理完選項之後繼續處理其他指令行參數了。

     例如:

Linux指令行與Shell腳本程式設計(續)

  5)  将選項标準化

  6) 獲得使用者輸入

     6.1) 基本的讀取

        read指令接受從标準輸入(鍵盤)或另外一個檔案描述符的輸入。在收到輸入後,read指令會将資料放進一個标準變量。下面是read指令最簡單的用法:

Linux指令行與Shell腳本程式設計(續)

      實際上,read指令包含了-p選項,允許你直接在read指令行指定提示符:

Linux指令行與Shell腳本程式設計(續)

     注意:read會将讀取的資料分别儲存到你指定的多個變量中。如: read -p "Please enter your info:" name age sex info。輸入的每一個資料值都會配置設定給表中的下一個變

    量。如果變量表在資料之前用完了,剩下的資料就會配置設定給最後一個變量。

     另外,你可以在read指令行中不指定變量。如果那麼做了,read指令會将它收到的任何資料都放進特殊環境變量REPLY中。

   6.2) 帶逾時機制的read

        使用read指令時有個危險,就是腳本很可能會等腳本使用者的輸入一直等下去。如果腳本必須執行下去,不管是否有資料輸入,你可以用-t選項來指定一個計時器。-t選項

   指定了read指令等待輸入的秒數。當計時器過期以後,read指令會傳回一個非零退出狀态碼。

     例如:

Linux指令行與Shell腳本程式設計(續)

      此外,可以讓read指令對輸入的字元計數,而非對輸入過程計時。當輸入的字元達到預設的字元數時,它會自動退出,将輸入的資料指派給變量:

Linux指令行與Shell腳本程式設計(續)

    上例中,将-n選項和值1一起使用,告訴read指令在接受單個字元後退出。隻要你按下單個字元回答後,read指令就會接受輸入并将它傳給變量,而不必按Enter鍵。

   6.3) 隐藏方式讀取

       有時你需要讀取腳本使用者的輸入,但不想輸入出現在螢幕上。典型的例子是輸入的密碼,但還有很多其他需要隐藏的資料類型。-s選項會阻止将傳給read指令的資料顯示在

   顯示器上(實際上,資料會被顯示,隻是read指令會将文本顔色設成跟背景色一樣)。這裡有個在腳本中使用-s選項的例子:

   6.4) 從檔案中讀取

      你也可以用read指令來讀取Linux系統上檔案裡儲存的資料。每次調用read指令會從檔案中讀取一行文本。當檔案中再沒有内容時,read指令會退出并傳回非零退出狀态碼。例如:

Linux指令行與Shell腳本程式設計(續)

  二、呈現資料

 1) 重定向資料

      1.1) 隻重定向錯誤

         STDERR檔案描述符被設成2。你可以選擇隻重定向錯誤消息,将該檔案描述符值放在重定向符号前。該值必須緊緊的放在重定向符号前,否則不會工作。

         # ls -al badfile 2> test4.log

      1.2) 重定向錯誤和資料

         # ls -al goodfile badfile 1>nomal.log 2>bad.log

 2) 在腳本中重定向輸出

     2.1) 臨時重定向

         在重定向到檔案描述符時,你必須在檔案描述符數字之前加一個and符号(&)。例如:

          # echo "This is an error message" >&2

     2.2)  永久重定向

        如果腳本中有大量資料需要重定向,那重定向每個echo語句就會很繁瑣。取而代之,你可以用exec指令告訴shell在腳本執行期間重定向某個特定檔案描述符。例如:

Linux指令行與Shell腳本程式設計(續)

3) 在腳本中重定向輸入

     exec指令允許你将STDIN重定向到Linux系統上的檔案中:

     # exec 0<testfile 

     例如:

Linux指令行與Shell腳本程式設計(續)

   4) 建立自己的重定向

         在腳本中重定向輸入和輸出時,并不局限于這3個預設的檔案描述符。在shell中最多可以有9個打開的檔案描述符。其他6個檔案描述符會從3排到8,并且當做輸入和輸出

    重定向都行。你可以将這些檔案描述符的任意一個配置設定給檔案,然後在腳本中使用它們。

         4.1) 建立輸出檔案描述符

           你可以用exec指令來給輸出配置設定檔案描述符。和标準的檔案描述符一樣,一旦你給一個檔案位置配置設定了另外一個檔案描述符,那個重定向就會一直有效,直到你重新分

        配。這裡有個在腳本中使用其他檔案描述符的簡單例子:

Linux指令行與Shell腳本程式設計(續)

         4.2) 重定向檔案描述符

           你可以配置設定另外一個檔案描述符給标準檔案描述符,反之亦然。這意味着你可以重定向STDOUT的原來位置到另外一個檔案描述符,然後将該檔案描述符重定向回STDOUT。可以了解為C語言中的指針概念。

        如下圖:

Linux指令行與Shell腳本程式設計(續)

          現在假如用另外一個檔案描述符3儲存monitor,那麼我們可以将STDOUT(1)重定向到一個檔案,然後再用3恢複STDOUT(1),使其在定向到monitor。

          如下例子:

Linux指令行與Shell腳本程式設計(續)

        再看輸入的例子:

Linux指令行與Shell腳本程式設計(續)

        4.3)  建立讀寫檔案描述符

            一般不建議這樣做。但是可以采用如下的方式:

            # exec 3<>testfile

        4.4) 關閉檔案描述符

          # exec 3>&-

          例如:

Linux指令行與Shell腳本程式設計(續)

 5) 列出打開檔案 lsof

 6) 阻止指令輸出 (重定向到/dev/null)

 7) 建立臨時檔案 mktemp

 8) 記錄消息(tee)

    # date | tee testout.log

    上面這條指令會同時在STDOUT與testout.log檔案中顯示目前的日期。

三、建立函數

1)  基本的腳本函數

    1.1) 建立函數 

          有兩種格式可以用來在bash shell腳本中建立函數。第一種格式采用關鍵字function,後跟配置設定該代碼塊的函數名:

          function name{

                 commands

          }

          bash shell腳本中定義函數的第二種格式跟在其他程式設計語言中定義函數很像:

         name(){

commands

         }

      1.2) 使用函數

       要在腳本中使用函數,在行上指定函數名就行了,跟使用其他shell指令一樣:

Linux指令行與Shell腳本程式設計(續)

2) 傳回值

    bash shell會把函數當做小型腳本,運作結束時會傳回一個特殊狀态碼。有3種不同的方法來為函數生成退出狀态碼。

    2.1) 預設退出狀态碼

         預設情況下,函數的退出狀态碼是函數中最後一條指令傳回的退出狀态碼。在函數執行結束後,你可以用标準的$?變量來決定函數的退出狀态碼.

     2.2) 使用return指令

       bash shell使用return指令來退出函數并傳回特定的退出狀态碼。return指令允許指定一個整數值來定義函數的退出狀态碼,進而提供了程式設計設定函數退出狀态碼的簡便途徑:

Linux指令行與Shell腳本程式設計(續)

    請記住如下兩點: 1) 函數一結束,就取傳回值 2)退出狀态碼必須在0~255之間.

      2.3) 使用函數輸出

        正如同可以将指令的輸出儲存到shell變量中一樣,也可以将函數的輸出儲存到shell變量中,可以用這種技術來擷取任何類型的輸出,并将其儲存到變量中。

      例如:

Linux指令行與Shell腳本程式設計(續)

3) 在函數中使用變量

   3.1) 向函數傳遞參數

         bash shell會将函數當做小型腳本來對待。這意味着你可以向函數傳遞參數,就跟普通腳本一樣。

         函數可以使用标準的參數環境變量來代表指令行上傳給函數的參數。例如,函數名會再$0變量中定義,函數指令行上的任何參數都會通過$1,$2等定義。也可以用特殊

    變量$#來判斷傳給函數的參數數目。

         在腳本中指定函數時,必須将參數和函數放在同一行。例如:

Linux指令行與Shell腳本程式設計(續)

   注意:盡管在函數中也使用了$1,$2這樣的環境變量,但是它卻不能直接從腳本的指令行來擷取參數值。你需要通過傳參方式來向函數傳遞參數。

   3.2) 在函數中處理變量

        給shell腳本程式員經常帶來麻煩的事是變量的作用域。作用域是什麼情況下變量可見。函數中定義的變量可以跟普通變量的作用域不同,也就是說,它們可以在腳本的

   其他部分隐藏起來。

        函數會有兩種類型的變量:

  • 全局變量
  • 局部變量

     3.2.1) 全局變量

            全局變量是在shell腳本中任何地方都有效的變量。如果你在腳本的主體部分定義了一個全局變量,那麼你可以在函數内讀取它的值。類似的,如果你在函數内定義了一

      個全局變量,你可以在腳本的主體部分讀取它的值。

          預設情況下,你在腳本中定義的任何變量都是全局變量,在函數外定義的變量可以在函數内正常通路。

Linux指令行與Shell腳本程式設計(續)

         但是,上面這種用全局變量的方式其實很危險。因為其有可能在函數内部更改掉一個值。

    3.2.2)  局部變量

        不用在函數中使用全局變量,函數内部使用的任何變量都可以被聲明為局部變量。要那麼做,隻要在變量聲明的前面加上local關鍵字就可以了:

        local temp

        也可以在給變量指派時在指派語句中使用local關鍵字:

        local temp=$ [ $value +5 ]

       local關鍵字保證了該變量隻局限在該函數中。如果腳本中在該函數之外有同樣名字的變量,那麼shell将會保持這兩個變量的值是分離的。例如:

Linux指令行與Shell腳本程式設計(續)

4) 數組變量和函數

    4.1) 向函數傳遞數組參數

          向腳本函數傳遞數組變量的方法會有些不好了解。将數組變量當做單個參數傳遞的話,它不會起作用。如果你試圖将該數組變量當成一個函數參數,函數隻會去數組變量

    的第一個值。

          要解決這個問題,你必須将該數組變量的值分解成單個值然後将這些值作為函數參數使用。在函數内部,你可以将所有的參數重組到新的數組變量中。例如:

Linux指令行與Shell腳本程式設計(續)

   再看下面一個例子:

Linux指令行與Shell腳本程式設計(續)

    4.2) 從函數傳回數組

        從函數裡向shell腳本傳回數組變量也用類似的方法。函數用echo語句來按正确的順序輸出單個數組值,然後腳本再将它們重新放進一個新的數組變量中:

Linux指令行與Shell腳本程式設計(續)

5) 函數遞歸

    請看如下求階乘的例子:

Linux指令行與Shell腳本程式設計(續)

6) 建立腳本函數庫

     這裡建立腳本函數庫很簡單,但是在引用腳本函數庫時必須用如下方法(例如我們建立了一個腳本函數庫mylib.sh)

     source ./mylib.sh

      或者

     . ./mylib.sh

    之是以我們要用上面的方法,是因為當我們直接運作一個腳本時,一般會在原來的shell中再開啟一個新的shell。這兩個shell是獨立的,是以不能直接調用。

7) 在指令行上使用函數

     有兩種方法。第一種是:

Linux指令行與Shell腳本程式設計(續)

   第二種是:

Linux指令行與Shell腳本程式設計(續)

8) 在..bashrc中定義函數

繼續閱讀