這裏還有上一篇,這部分是基礎:
http://www.javashuo.com/article/p-garoayed-bu.html面試
使用getopt命令,能夠解析任何命令行選項和參數,可是用法比較複雜。getopt的命令用法以下:shell
$ getopt --help 用法: getopt optstring parameters getopt [options] [--] optstring parameters getopt [options] -o|--options optstring [options] [--] parameters 選項: -a, --alternative 容許長選項以 - 開始 -h, --help 這個簡短的用法指南 -l, --longoptions <長選項> 要識別的長選項 -n, --name <程序名> 將錯誤報告給的程序名 -o, --options <選項字符串> 要識別的短選項 -q, --quiet 禁止 getopt(3) 的錯誤報告 -Q, --quiet-output 無正常輸出 -s, --shell <shell> 設置 shell 引用規則 -T, --test 測試 getopt(1) 版本 -u, --unquoted 不引用輸出 -V, --version 輸出版本信息 $
用法一共有3種格式,下面都會用到。 數組
先看第一個最簡單的格式:bash
getopt optstring parameters
第一部分是命令名。
第二部分optstring(選項字符串),是這個命令解析的格式。
第三部分parameters(getopt命令的參數),就是須要解析的內容。
所以,getopt會按照 optstring 的設置,將 parameters 解析爲相應的選項和參數。參考示例來理解:ide
$ getopt ab:cd -ad value1 -b best1 value2 value3 -a -d -b best1 -- value1 value2 value3 $
主要理解 ab:cd
的意義。
這裏定義的都是短選項,4個字母表明有4個選項。b後面的冒號表示這個選項須要一個參數。若是不給選項b一個參數,就會報錯:工具
$ getopt ab:cd -ad value1 -b getopt:選項須要一個參數 -- b -a -d -- value1 $
使用雙破折線
若是添加了雙破折線,那麼無輪後面是什麼,都會做爲參數而不是選項來處理:測試
$ getopt ab:cd -- -ad value1 -b best1 value2 value3 -- -ad value1 -b best1 value2 value3 $ getopt ab:cd -ad value1 -- -b best1 value2 value3 -a -d -- value1 -b best1 value2 value3 $
這依然是是命令用法的第一種格式,雙破折線是parameters內容的一部分。
雙破折線出現位置以前的內容按照optstring的設置來解析,以後的內容一概認爲是參數。即便有相似選項的內容,被認做爲是參數。 ui
參數包含空格的問題
第一種格式和第2、第三種在功能上也是有區別的。這裏輸出的參數都是不帶引號的。而另外兩種格式輸出的參數都是帶引號的。
重要的區別不在引號上,而是這種用法不支持處理帶空格和引號的參數值。它會將空格看成參數分隔符,而不是根據雙引號將兩者看成一個參數。 命令行
參考上面的示例,加上長選項的支持。使用長選項的示例以下:code
$ getopt -o ab:cd --long arga,argb:,argc,argd -- -ad -b best --argd value1 value2 -a -d -b 'best' --argd -- 'value1' 'value2' $
這是命令用法的第三種格式。
-o 表示定義短選項
--long 實際上是--longoptions,不過省略任意個字母程序都能認識。或者也能夠用-l。這個是指定長選項的。全部內容都要連起來,不能有空格。選項之間用逗號隔開。定義完以後,在用雙破折號隔開,後面的內容就是parameters。
以前已經試過一次解析錯誤的報告了:
$ getopt ab:cd -ad value1 -b getopt:選項須要一個參數 -- b -a -d -- value1 $
這裏錯誤報告的是getopt錯誤,能夠把這個默認的內容替換掉。通常是換成執行的腳本的名字。
這裏使用命令用法的第二種格式,把 optstring 和 parameters 都放到雙破折線後面:
$ getopt -- ab:cd -ad value1 -b best1 -a -d -b 'best1' -- 'value1' $
這樣在雙破折線前面就可加getopt命令的選項,這裏要指定-n選項:
$ getopt -n test.sh -- ab:cd -ad value1 -b test.sh:選項須要一個參數 -- b -a -d -- 'value1' $
這裏看到包裹錯誤是,名字已經被替換掉了。
在腳本中,可使用 $(basename $0)
或者直接用\$0。
禁止錯誤報告
還有一個-q參數,能夠禁止錯誤報告,解析錯誤的選項和參數將被丟棄:
$ getopt -n test.sh -q -- ab:cd -ad value1 -b -a -d -- 'value1' $
還有一種可選參數,使用兩個冒號。這個選項能夠有一個或零個參數:
$ getopt -o a::bc: -l arga::,argb,argc: -- -a value1 --arga value2 -a '' --arga '' -- 'value1' 'value2' $ getopt -o a::bc: -l arga::,argb,argc: -- -avalue1 --arga=value2 -a 'value1' --arga 'value2' -- $
第一次執行是傳遞的參數是錯誤的。由於是可選參數,參數和值之間不能有空格隔開,不然會有歧義。必需要連在一塊兒才能認爲是前一個選項的參數。不然就被認做是獨立的參數了。
getopt 命令的選項所指定的選項字符串的規則:
如今已經能夠用getopt命令,將命令行參數按照規定的格式解析成規整的格式了。而且在解析過程當中,還能發現參數格式錯誤的狀況並報告。
接下來就是在腳本中使用通過getopt命令解析後的參數了。
要在腳本中使用getopt。首先,要用getopt命令生成格式化後的版原本替換已有的命令行選項和參數。須要用到set命令。
set命令可以處理shell中的各類變量。具體不展開,這裏只用了這個命令的一個選項,雙破折線(--)。效果是將命令行參數替換成set命令的參數值。
而後,該方法會將原始腳本的命令行參數傳給getopt命令執行,以後再將getopt命令的輸出傳給set命令,用getopt格式化後的命令行參數來替換原始的命令行參數:
set -- $(getopt ab:cd "$@")
如今原始的命令行參數變量的值會被getopt命令的輸出替換。而getopt已經爲咱們格式化好了命令行參數。
在以前編寫的腳本的基礎上,只要在開頭加上一行代碼,就能夠直接使用了:
set -- $(getopt a:b:s:u "$@")
加上這句後,就是讓後續的代碼處理getopt返回的參數,而不是調用命令時的命令行參數。
驗證效果:
$ ./format.sh -u -a after -b befor value1 value2 value3 BEFOR_VALUE1_AFTER BEFOR_VALUE2_AFTER BEFOR_VALUE3_AFTER $ ./format.sh -u -a after -b befor value1 "value2 value3" value4 BEFOR_VALUE1_AFTER BEFOR_VALUE2_AFTER BEFOR_VALUE3_AFTER BEFOR_VALUE4_AFTER $
第二條命令並不能處理帶空格的參數,由於這裏使用的是getopt的第一種格式。
要處理空格,就須要使用第二種格式(或者第三種),將命令修改成以下:
set -- $(getopt -- a:b:s:u "$@")
簡單的在最前面加上雙破折線就行了。這條語句是錯誤的,後面還要修改。
再來驗證一下:
$ ./format.sh -u -a after -b befor value1 "value2 value3" value4 'BEFOR'_'VALUE1'_'AFTER' 'BEFOR'_'VALUE2_'AFTER' 'BEFOR'_VALUE3'_'AFTER' 'BEFOR'_'VALUE4'_'AFTER' $
使用第2、第三種格式,會用引號來限定參數的內容。可是引號干擾了set命令。
使用 eval 命令
這裏出現了一個新的問題,不但沒有正確的處理空格,輸出的內容還有額外的引號。空格的問題先放一放,這裏須要用到eval命令來解決新問題。
eval 命令用於將其後的內容做爲單個命令讀取和執行,這裏用於處理getopt命令生成的參數的轉義字符。
關於eval命令,還有一種使用的情景。有時候在腳本中拼接出來的字符串即便打印出來看正確。而且直接複製、粘貼在交互界面中也能正確讀被當作命令運行。可是卻沒法在腳本中被執行。這個時候就可使用eval命令來解決。它可以把字符串當作命令來執行。
在腳本中經過各類引用和判斷拼接出一個複雜的命令的時候,有時候就會出現沒法執行的狀況。這時候就直接賦值、粘貼去交換界面試一下,若是拼接的結果自己沒問題,那麼加上eval命令後,應該就能用運行。
修改命令以下:
eval set -- $(getopt -- a:b:s:u "$@")
再次驗證:
$ ./format.sh -u -a after -b befor value1 "value2 value3" value4 BEFOR_VALUE1_AFTER BEFOR_VALUE2 VALUE3_AFTER BEFOR_VALUE4_AFTER $
第一種格式加上eval命令也是沒有問題的,因此能夠無腦用上。
只要能正確的使用getopt的第二種或第三種格式,那麼參數包含空格的問題也就解決了。看上一小節。
執行命令時,使用錯誤的參數,當前的效果以下:
$ ./format.sh -u -w -a after -b befor value1 "value2 value3" value4 getopt:無效選項 -- w BEFOR_VALUE1_AFTER BEFOR_VALUE2 VALUE3_AFTER BEFOR_VALUE4_AFTER $
解析發現問題了,而且報告了,可是腳本沒有終止,而是繼續執行。若是要判斷出解析錯誤,就須要使用\$?參數。而後退出腳本則是用exit命令。
這裏直接直接使用\$?並沒有法獲取到參數解析錯誤的結果。由於此時的結果是set命令(也多是eval命令)的執行結果,而getopt是再前一條的命令。
解決這個問題,要先把getopt命令執行一遍,進行判斷。而後再用set調用一遍,能夠直接使用以前執行的結果:
getopt_cmd=$(getopt -n $(basename $0) -- a:b:s:u "$@") [ $? -ne 0 ] && exit 1 eval set -- "$getopt_cmd"
這裏還加上了報告錯誤時名稱的定義。exit退出時也要指定退出狀態爲非0,由於是運行錯誤。
驗證效果:
$ ./format.sh -v -a after -w -b format.sh:無效選項 -- v format.sh:無效選項 -- w format.sh:選項須要一個參數 -- b $ echo $? 1 $
如今解析有問題後,就會直接退出。
這裏加上長選項以及可選參數的功能。
多加了一個參數 -m, --mark
因爲指定使用什麼鏈接符:
參數比較多,加了 -h, --help
選項打印參數說明。
完整代碼以下:
$ cat format.sh #!/bin/bash mark="" # 鏈接符號 prefix="" # 前綴 base="test" # 默認字符串 suffix="" # 後綴 upper=off # 是否大寫 # 顯示聲明一下這是個數組變量,其實沒有必要 declare -a names # 須要格式化輸出的全部原始字符串 # 打印的幫助信息 help_str=" 參數說明: -h, --help: 打印幫助信息 -m, --mark [鏈接符]: 使用鏈接符,默認是下劃線(_),能夠指定 -a, --after string: 添加後綴 -b, --befor string: 添加前綴 -s, --string string: 指定中間的字符串,默認是「test」 -u, --upper: 全大寫輸出 " # 解析命令行參數 getopt_cmd=$(getopt -o m::ha:b:s:u --long mark::,help,after:,befor:,string:,upper -n $(basename $0) -- "$@") [ $? -ne 0 ] && exit 1 eval set -- "$getopt_cmd" # 解析選項 while [ -n "$1" ] do case "$1" in -m|--mark) case "$2" in "") mark="_" shift ;; *) mark="$2" shift ;; esac ;; -h|--help) echo -e "$help_str" exit ;; -a|--after) suffix="$2" shift ;; -b|--befor) prefix="$2" shift ;; -s|--string) base="$2" shift ;; -u|--upper) upper=on ;; --) shift break ;; *) echo "$1 is not an option" exit 1 ;; # 發現未知參數,直接退出 esac shift done # 解析參數 while [ -n "$1" ] do names=("${names[@]}" "$1") shift done names[0]=${names[0]:-$base} for name in "${names[@]}" do # 添加前綴和後綴 output="${prefix:+${prefix}${mark}}${name}${suffix:+${mark}${suffix}}" # 判斷是否要全大寫輸出 if [ $upper = on ] then output=${output^^} fi # 輸出結果 echo "$output" done $
驗證效果:
$ ./format.sh -a after -b befor VALUE1 "VALUE2 VALUE3" VALUE4 beforVALUE1after beforVALUE2 VALUE3after beforVALUE4after $ ./format.sh -a after -b befor --mark befor_test_after $ ./format.sh -a after -b befor --mark="||" -u BEFOR||TEST||AFTER $ ./format.sh -a after -b befor --mark="||" -u --help 參數說明: -h, --help: 打印幫助信息 -m, --mark [鏈接符]: 使用鏈接符,默認是下劃線(_),能夠指定 -a, --after string: 添加後綴 -b, --befor string: 添加前綴 -s, --string string: 指定中間的字符串,默認是「test」 -u, --upper: 全大寫輸出 $
有getopt就夠用了。順便再簡單講下getopts。
getopts功能上差一點,不過封裝的更高級,用起來更簡單,須要的代碼也會少一點。
getopts是另外一個解析命令行參數的工具。它是Bash的內部命令。
它的優點在於:
不支持長選項:
getopts 不能解析 GUN 風格的長選項(--long),也不能解析 XF86 風格的長選項(-long)
getopt 是將選項和參數處理後只生成一個輸出。咱們還要用 set 來完成傳遞的工做。
getopts 可以和已有的shell參數變量配合默契。每次調用時,一次只處理命令行上檢測到的一個參數。處理以後,它會退出並返回一個大於0的退出狀態碼。這樣就很是方便的能夠在while循環中使用。
getopts 會使用到一下3個變量:
OPTIND: 存放下一個要處理的參數的索引。這是 getopts 在調用過程當中記住本身狀態的方式。
OPTARG: 由 getopts 找到的選項所對應的參數。
OPTERR: 值爲0或1。指示Bash是否應該顯示由 getopts 產生的錯誤信息。
getopts 命令的基本語法:
getopts 選項字符串 名稱 [參數]
選項字符串(OPTSTRING):getopts 會有哪些選項,哪些是有參數的(選項後有冒號)
名稱(VARNAME):getopts 會將找到的選項賦值給這個名稱的變量
參數(ARGS):通常狀況向缺省,getopts會去解析腳本調用時的全部的參數。若是執行了這個參數,getopts就不解析傳遞給腳本的參數了,而是解析這裏的參數。
getopts 不會移動變量。在處理完全部的選項後,命令就會中止,並將參數留給咱們來繼續處理。此時能夠先用shit命令配合OPTIND的值來移動到第一個參數的位置:
shift $[ $OPTIND - 1 ]
getopts命令支持兩種錯誤報告模式:
對於產品中的腳本,推薦使用抑制錯誤報告模式。
詳細錯誤報告模式
在詳細錯誤報告模式下,若是 getopts 遇到了一個無效的選項,VARNAME 的值會被設置爲問號(?),而且變量 OPTARG 不會被設置。若是須要的參數沒找到,VARNAME的值也會被設置爲問號(?),變量 OPRARG 也不會被設置,而且會打印一個錯誤信息。
抑制錯誤報告模式
在抑制錯誤報告模式下,若是 getopts 遇到一個無效的選項,VARNAME 的值會被設置爲問號(?),而且變量 OPTARG 會被設置爲選項字符。若是須要的參數沒找到,VARNAME的值會被設置爲冒號(:),而且變量 OPTARG 中會包含選項字符。
要使用抑制錯誤報告模式,只須要在調用 getopts 時,設置選項字符串(OPTSTRING)時以冒號開頭便可。下面的例子用的就是一直錯誤報告模式。
這裏使用抑制錯誤報告模式,因此須要本身分析而且報告解析錯誤。都在代碼裏了:
$ cat say_hello.sh #!/bin/bash defaultname="nobody" # 默認的名字 declare -a names # 存放名字的數組 hello="hello" # 打招呼的用語 end="!" # 結束的內容 tittle=off # 是否首字母大寫 # 解析選項 while getopts :n:h:e:t opt do case "$opt" in n) defaultname="$OPTARG" ;; h) hello="$OPTARG" ;; e) end="$OPTARG" ;; t) tittle=on ;; :) # 沒有爲須要參數的選項指定參數 echo "This option -$OPTARG requires an argument." exit 1 ;; ?) # 發現了無效的選項 echo "-$OPTARG is not an option" exit 2 ;; esac done # 解析參數 shift $[ $OPTIND -1 ] # 移動到第一個參數的位置 # 此次用for循環遍歷 for arg in "$@" do names=("${names[@]}" "$arg") done names[0]=${names[0]:-$defaultname} for name in "${names[@]}" do [ "$tittle" = on ] && output="${hello^} ${name^} $end" || output="$hello $name $end" echo "${output}" done $
驗證執行以下:
$ ./say_hello.sh hello nobody ! $ ./say_hello.sh -n adam hello adam ! $ ./say_hello.sh -n adam -h hi -e. -t Hi Adam . $ ./say_hello.sh -h hi -e. -t adam bob clark Hi Adam . Hi Bob . Hi Clark . $ ./say_hello.sh -a -h hi -e. -t adam bob clark -a is not an option $ ./say_hello.sh -h This option -h requires an argument. $
選項和參數不能混排:
$ ./say_hello.sh adam hello adam ! $ ./say_hello.sh adam -t hello adam ! hello -t ! $
支持雙破折線:
$ ./say_hello.sh -t adam Hello Adam ! $ ./say_hello.sh -t -- adam Hello Adam ! $
比較下來,使用起來會比getopt方便不少,不過功能上也差了不少,可選參數(雙冒號::)應該也是不支持的。另外,若是熟悉getopt的話,每一步的操做都是本身的代碼控制的。而getopts就簡化了不少地方,好比不會調用shift移動變量。
有些字母選項在Linux世界裏已經有了某種程度的標準含義。若是在shell腳本中支持這些選項,就應該使用標準的字母來定義。 下面是一些命令行選項中常常會用到的選項和含義: |
選項 | 描述 |
---|---|---|
-a | 顯示全部對象(顯示隱藏) | |
-c | 生成一個計數 | |
-d | 指定一個目錄 | |
-e | 擴展一個對象 | |
-f | 指定讀入數據的文件 | |
-h | 顯示命令的幫助信息 | |
-i | 忽略文本大小寫 | |
-l | 產生輸出的長格式版本 | |
-n | 使用非交互模式(批處理) | |
-o | 將全部輸出重定向到指定的輸出文件 | |
-q | 以安靜模式運行 | |
-r | 遞歸地處理目錄和文件 | |
-s | 以安靜模式運行 | |
-v | 生成詳細輸出 | |
-x | 排除某個對象 | |
-y | 對全部問題回答yes |