天天看點

shell程式設計——getopt1.指令行選項的那些事

寫shell腳本的時候,通過while、case、shift來設計腳本的指令行選項是一件比較麻煩的事,因為Unix指令行的選項和參數自由度很高,支援短選項和長選項,參數可能是可選的,選項順序可能是無所謂的,等等。

bash下的getopt指令可以解析指令行的選項和參數,将散亂、自由的指令行選項和參數進行改造,得到一個完整的、規範化的參數清單,這樣再使用while、case和shift進行處理就簡單的太多了。

getopt有不同的版本,本文介紹的是它的增強版(enhanced),相比傳統的getopt(也成為相容版本的getopt),它提供了引号保護的能力。另外,除了不同版本的getopt,bash還有一個内置指令getopts(注意,有個尾随的字元s),也用來解析指令行選項,但隻能解析短選項。

要驗證安裝的getopt是增強版的還是傳統版的,使用

getopt -T

判斷即可。如果它什麼都不輸出,則是增強版,此時它的退出狀态碼為4。如果輸出"--",則是傳統版的getopt,此時它的退出狀态碼為0。如果想在腳本中進行版本檢查,可以參考如下代碼:

getopt -T &>/dev/null;[ $? -ne 4 ] && { echo "not enhanced version";exit 1; }           

1.指令行選項的那些事

在學習getopt如何使用之前,必須先知道指令行的一些常識。這些,都可以通過getopt來實作,但有些實作起來可能會比較複雜。

1.區分option、parameter、argument、option argument和non-option parament

parameter和argument都表示參數,前者通常表示獨立性的參數,後者通常表示依賴于其它實體的參數。parameter的含義更廣,argument可以看作parameter的一種。

例如,定義函數時

function foo(x,y){CODE}

,函數的參數x和y稱為parameter。調用函數并傳遞參數時,

foo(arg1,arg2)

中的arg1和arg2都是依賴于函數的,稱為argument更合适,當然也可以稱為更廣泛的parameter。

再例如,一個指令行:

tar -zcf a.tar.gz /etc/pki           

粗分的話,

-z

-c

-f

a.tar.gz

/etc/pki

都可以稱為parameter。細分的話:

  • "-z -c -f"稱為選項,即option
  • a.tar.gz是選項"-f"的選項參數(傳遞給選項的參數),依賴于選項,稱為argument更合适,更嚴格的稱呼是option argument
  • /etc/pki既不屬于選項,也不屬于某個選項的參數,它稱為非選項類型的參數,對應的名稱為non-option parameter

本文要介紹的是getopt,是以隻考慮指令行參數的情況。

2.短選項和長選項以及它們的"潛規則"

Linux中絕大多數指令都提供了短選項和長選項。一般來說,短選項是隻使用一個"-"開頭,選項部分隻使用一個字元,長選項是使用兩個短橫線(即"--")開頭的。

例如"-a"是短選項,"--append"是長選項。

一般來說,選項的順序是無所謂的,但并非絕對如此,有時候某些選項必須放在前面,必須放在某些選項的前面、後面。

一般來說,短選項:

  • 可以通過一個短橫線"-"将多個短選項連接配接在一起,但如果連在一起的短選項有參數的話,則必須作為串聯的最後一個字元。

    例如"-avz"其實會被解析為"-a -v -z",

    tar -zcf a.tar.gz

    串聯了多個短選項,但"-f"選項有參數a.tar.gz,是以它必須作為串聯選項的最後一個字元。
  • 短選項的參數可以和選項名稱連在一起,也可以是用空白分隔。例如

    -n 3

    -n3

    是等價的,數值3都是"-n"選項的參數值。
  • 如果某個短選項的參數是可選的,那麼它的參數必須緊跟在選項名後面,不能使用空格分開。至于為什麼,見下面的第3項。

一般來說,長選項:

  • 可以使用等号或空白連接配接兩種方式提供選項參數。例如

    --file=FILE

    --file FILE

  • 如果某個長選項的參數是可選的,那麼它的參數必須使用"="連接配接。至于為什麼,見下面的第3項。
  • 長選項一般可以縮寫,隻要不産生歧義即可。

例如,ls指令,以"a"開頭的長選項有3個。

$ ls --help | grep -- '--a' 
  -a, --all                  do not ignore entries starting with .
  -A, --almost-all           do not list implied . and ..
      --author               with -l, print the author of each file           

如果想要指定

--almost-all

,可以縮寫為

--alm

;如果想要指定

--author

--au

。如果隻縮寫為"--a",bash将給出錯誤提示,長選項出現歧義:

$ ls --a
ls: option '--a' is ambiguous; possibilities: '--all' '--author' '--almost-all'
Try 'ls --help' for more information.           

3.不帶參數的選項、可選參數的選項和帶參數的選項

有不同類型的指令行選項,這些選項可能不需要參數,也可能參數是可選的,也可能是強制要求參數的。

前面說了,如果某個選項的參數是可選的,那麼它的參數必須不能使用空格将參數和選項分開。如果使用空格分隔,則無法判斷它的下一個元素是該選項的參數還是非選項類型的參數。

例如,

-c

--config

選項的參數是可選的,要向這兩個選項提供參數,必須寫成

-cFILE

--config=FILE

,如果寫成

-c FILE

--config FILE

,那麼getopt無法判斷這個FILE是提供給選項的參數,還是非選項類型的參數。

一般來說,使用可選參數的情況非常少,至少我目前回憶不起來這樣的指令。

4.使用"--"将選項(及它們的選項參數)與非選項類型參數進行分隔

unix的指令行中,總是可以在非選項類型的參數之前加上"--",表示選項和選項參數到此為止,後面的都是非選項類型的參數。

例如:

seq -w -- 3
seq -w -- 1 3           

分别表示3和"1 3"是seq的非選項類型參數,而"--"前面的一定是選項或選項參數。

5.指令行參數中的短橫線開頭的并不一定總是短選項,也可能是負數參數

例如seq指令:

seq -w -5 -1 5           

其中-5和-1都是負數非選項類型的參數。

6.選項的依賴性和互斥性

有些指令的選項是有依賴性和互斥性的。比如某個選項要和另一個選項一起使用,某個選項不能和另一個選項一起使用。

例如

--manage --remove

,隻有在使用了

--manage

的前提下才能使用

--remove

,否則就應該報錯。

7.模式化(子產品化)類型的選項

很多unix指令都将選項進行子產品化設計。例如ip指令,address模式、route模式、link模式等等。

ip addr OPTIONS
ip route OPTIONS
ip link OPTIONS 
ip neigh OPTIONS           

8.其他特性的選項

有些指令還有比較個性化的選項,比如head指令,

-n NUM

選項,即可以指定為

-3

,也可以指定為

-n 3

-n3