Linux Bash Shell學習(七):shell程式設計基礎——執行Shell指令碼、function

愷風發表於2009-12-15

  本文也即《Learning the bash Shell》3rd Edition的第四章Basic Shell Programming之讀書筆記,但我們將不限於此。

執行shell指令碼程式

  一個包含shell命令的指令碼就是一個shell程式,例如.bash_profile。我們建立shell指令碼,允許的時候可以通過兩種方式:一、source ;二、只敲入檔名,按回車就可以執行,這種方式更為便捷。我們需要將檔案放置在命令搜尋路徑下(在Linux Bash學習(六):設定環境引數 中介紹),否則需要指出絕對路徑,例如在當前目錄,使用./, 另外我們還需要將檔案的許可權設定為可執行檔案,採用$ chmod +x 的方式設定,+x表示增加執行的許可權。

  這兩種方式是由區別的。採用source就如同我們在terminal上敲入命令一樣。採用後一種方式,系統執行一個子程式,它copy了shell,叫作subshell,主shell等待子shell執行結束。另外我們設定讓子shell後臺執行,即 &

  這個區別還體現在export 的使用上,例如我們設定aa=hello, world,這個變數在命令列中有效,但是在指令碼檔案是無效,需要使用export aa='hello, world ' 來保證在subprocess中也是有效,即可以用於指令碼檔案中。

函式Functions

  使用function有兩個原因:一、function存放在系統的記憶體,所以呼叫的使用,效率更高;二、更好地組織長的bash,使之模組化。定義function,有兩個方式:

function functname {
    shell commands
}
functname ( ){
    shell commands
}

  上面兩種方式沒有區別。我們可以通過unset –f functname 來刪除函式的定義。 當我們定義一個function,將它的名字和定義存放在記憶體,我們可以象呼叫shell指令碼的方式來呼叫它。我們通過declare -f 來檢視當前已經定義的function情況,如果我們只檢視function名字,使用declare -F 。我們可以在命令列中敲入一個function來檢驗,如果我們將function定義在一個檔案中,例如在檔案a,可以用source a 來是指生效,如果我們將a設定為可執行檔案,這function的定義只在該指令碼中有效,如果需要使之仍然有效,保留環境,採用$. ./a ,而不只是$./a

Function作為一個整體執行,不會分割為子程式,此外Function的優先順序別高於指令碼。如果有重名,優先順序別依次如下:

  1. Aliases
  2. 關鍵字,例如function,if,for
  3. Functions
  4. Built-ins,例如cd,type
  5. 在命令搜尋路徑PATH下的指令碼和可執行檔案

  如果我們需要檢視所使用命令屬於哪種,用type name ,例如aa是個alias(表示pwd),同樣我們也定義了它作為一個function,根據優先順序別,aa優先作為alias,type aa,我們可以得到aa is aliased to ‘pwd’,可以用type –a(或者-all) name ,來檢視aa代表的所有含義。如果使用非最優先順序別或者重新定義優先順序別的先後順序,在書的第7章,我們先放下此話題不表。上述的命令將顯示詳細內容,可以用type –t name 的方式,檢視型別,將返回alias | keyword | function | builtin | file。type –p name 用於檢視file的路徑,如果型別不是file將沒有返回,而type –P name 則強制查詢file的路徑。例如一個重名,它是一個alias,也是一個在PATH目錄下的可執行檔案。-p則沒有返回,-P這返回檔案的絕對路徑。

位置引數

  在指令碼命令中,有時是帶有引數,這些引數可以通過位置變數來獲取。例如我們有個指令碼檔案叫做test,執行的使用帶引數,即$./test param1 param2 param3,我們可以在指令碼中來獲取引數的值。使用$N,其中$0是./test,它表示執行的指令碼名字,剩餘的為所帶引數,$1為param1,$2為param2,如此類推,如果N大於實際引數的數目,為空。我們一般將位置變數是從$1開始。

$* 表示所有引數組成的一個字串,在上面的例子中,為param1 param2 param3。這些引數之間的間隔是IFS的首個字母即空格,IFS包括TAB,空格,換行等字元。

$@ 等同於"$1" "$2"... "$N"。

$# 表示引數的個數,在上面的例子中為3。

  這些位置變數都是隻讀,不能賦值的。$*和$@非常相似,一般而言輸出是一樣的。$*的分割是在IFS的字元,我們可以重新定義IFS的字元,將導致不同的輸出結果。我們在指令碼中定義IFS=, ,即可將輸出改用逗號做為分割,這個指令碼名字為a,執行./a h1 h2,則$#=2,表示有兩個引數,$@為h1 h2,$*為h1,h2 ,輸出結果不一樣。

  同樣這些位置變數可以用於function中,並對function的引數進行體現,即是local的,屬於function,但是$0是例外,他表示指令碼的名字,這個引數是gobal的。下面是個例子。指令碼a內容如下:

hello ()
{
    var="hello";
    echo "Hello"
    echo "Hello: param num is $#"
    echo "Hello: $@"
    echo "Hello: $*"
    echo "Hello: $0 $1 $2"
    echo "Hello: var=$var";
}
var="hello";
echo "main: param num is $#"
echo "main: $@"
echo "main: $*"
echo "main: $0 $1 $2"
echo "main: var=$var";
hello h1 h2

 

  我們執行./a a1 a2 a3

main: param num is 3
main: a1 a2 a3
main: a1 a2 a3
main: ./a a1 a2
main: var=main
Hello
Hello: param num is 2
Hello: h1 h2
Hello: h1 h2
Hello: ./a h1 h2
Hello: var=hello

  除了$0外,所有的位置變數都是local的,即如果在function中,則在function中有效。同樣,使用者定義的其他變數,也是local的,即在最近的{}內有效。見上面例子中的使用者變數var。我們可以在function中定義var加上local來特別標明這是一個本地引數,上面的例子在函式hello,可以使用local var=”hello”

避免歧義的變數定義

  實際上我們使用$varname,是${varname} 的一個縮寫。有時我們不能使用縮寫,例如我們需要獲取第10個引數,使用$10,這實際是第一個引數加上“0”,需要使用${10}。又例如,我們希望顯示程式號,並在後面僅跟_、字母或者數字,我們使用echo $UID_,會見UID_看作一個整體,即echo ${UID_},因此是空,需要使用echo ${UID}_,假設程式號為1000,則為1000_。如果一個變數後面緊跟著_、字母或者數字,安全起見,需要用大括號。

相關連結: 我的Linux操作相關文章

相關文章