1. 程式人生 > 其它 >bash/shell 解析命令列引數工具:getopts/getopt

bash/shell 解析命令列引數工具:getopts/getopt

bash 指令碼中,簡單點的引數選項,我們可以直接用位置引數 $1 $2 這樣來獲取處理了,例如下面這段程式碼片段:

optionParam=$1
baseHdfsPath=$2
echo $optionParam|grep -qE '^(-d|-l)$' || usage
echo $baseHdfsPath|grep -qE '^/' || usage

if [[ $optionParam == "-l" ]]
then
	echo --------------------$startTime----------------------
	listDir "$baseHdfsPath"
	echo --------------------`date +'%F %T'`----------------------
else
	echo --------------------$startTime----------------------
	downLoadFiles "$baseHdfsPath"
	echo --------------------`date +'%F %T'`----------------------
fi

但是如果你的引數選項很多,比如 rsync、wget 等動輒幾十上百的引數選項,那就必須用專業的工具來處理了,在 bash/shell 中我們一般用:getopts/getopt 

1、bash 內建的 getopts:

先看簡單的例子:

#!/bin/bash
while getopts 'd:Dm:f:t:' OPT; do
    case $OPT in
        d)
            DEL_DAYS="$OPTARG";;
        D)
            DEL_ORIGINAL='yes';;
        f)
            DIR_FROM="$OPTARG";;
        m)
            MAILDIR_NAME="$OPTARG";;
        t)
            DIR_TO="$OPTARG";;
        ?)
            echo "Usage: `basename $0` [options] filename"
    esac
done

shift $(($OPTIND - 1))

getopts後面的字串就是可以使用的選項列表,每個字母代表一個選項,後面帶:的意味著選項除了定義本身之外,還會帶上一個引數作為選項的值,比如d:在實際的使用中就會對應-d 30,選項的值就是30;getopts字串中沒有跟隨:的是開關型選項,不需要再指定值,相當於true/false,只要帶了這個引數就是true。如果命令列中包含了沒有在getopts列表中的選項,會有警告資訊,如果在整個getopts字串前面也加上個:,就能消除警告資訊了。

使用getopts識別出各個選項之後,就可以配合case來進行相應的操作了。操作中有兩個相對固定的“常量”,一個是OPTARG,用來取當前選項的值,另外一個是OPTIND,代表當前選項在引數列表中的位移。注意case中的最後一個選擇──?,代表這如果出現了不認識的選項,所進行的操作。 選項引數識別完成之後,如果要取剩餘的其它命令列引數,可以使用shift把選項引數抹去,就像例子裡面的那樣,對整個引數列表進行左移操作,最左邊的引數就丟失了(已經用case判斷並進行了處理,不再需要了),位移的長度正好是剛才case迴圈完畢之後的OPTIND - 1,因為引數從1開始編號,選項處理完畢之後,正好指向剩餘其它引數的第一個。在這裡還要知道,getopts在處理引數的時候,處理一個開關型選項,OPTIND加1,處理一個帶值的選項引數,OPTIND則會加2。 最後,真正需要處理的引數就是$1~$#了,可以用for迴圈依次處理。

使用getopts處理引數雖然是方便,但仍然有兩個小小的侷限: 1.選項引數的格式必須是-d val,而不能是中間沒有空格的-dval。 2.所有選項引數必須寫在其它引數的前面,因為getopts是從命令列前面開始處理,遇到非-開頭的引數,或者選項引數結束標記--就中止了,如果中間遇到非選項的命令列引數,後面的選項引數就都取不到了。 3.不支援長選項, 也就是--debug之類的選項 再看個例項:

#!/bin/bash
echo 初始 OPTIND: $OPTIND

while getopts "a:b:c" arg #選項後面的冒號表示該選項需要引數
do
    case $arg in
        a)
			echo "a's arg:$OPTARG" #引數存在$OPTARG中
			;;
        b)
			echo "b's arg:$OPTARG"
			;;
        c)
			echo "c's arg:$OPTARG"
			;;
        ?)  #當有不認識的選項的時候arg為?
			echo "unkonw argument"
			exit 1
		;;
    esac
done

echo 處理完引數後的 OPTIND:$OPTIND
echo 移除已處理引數個數:$((OPTIND-1))
shift $((OPTIND-1))
echo 引數索引位置:$OPTIND
echo 準備處理餘下的引數:
echo "Other Params: $@"

結果:

june@Win7 192.168.1.111 02:32:45 ~ >
bash b.sh -a 1 -b 2 -c 3  test -oo xx -test
初始 OPTIND: 1
a's arg:1
b's arg:2
c's arg:
處理完引數後的 OPTIND:6
移除已處理引數個數:5
引數索引位置:6
準備處理餘下的引數:
Other Params: 3 test -oo xx -test
june@Win7 192.168.1.111 02:32:49 ~ >
bash b.sh -a 1 -c 3 -b 2 test -oo xx -test   # 非引數選項注意順序與值,不要多傳
初始 OPTIND: 1
a's arg:1
c's arg:
處理完引數後的 OPTIND:4
移除已處理引數個數:3
引數索引位置:4
準備處理餘下的引數:
Other Params: 3 -b 2 test -oo xx -test
june@Win7 192.168.1.111 02:33:14 ~ >
bash b.sh -a 1 -c -b 2 test -oo xx -test
初始 OPTIND: 1
a's arg:1
c's arg:
b's arg:2
處理完引數後的 OPTIND:6
移除已處理引數個數:5
引數索引位置:6
準備處理餘下的引數:
Other Params: test -oo xx -test
june@Win7 192.168.1.111 02:33:22 ~ >

2、外部強大的引數解析工具:getopt

先來看下getopt/getopts的區別 1. getopts是bash內建命令的, 而getopt是外部命令 2. getopts不支援長選項, 比如: --date 3. 在使用getopt的時候, 每處理完一個位置引數後都需要自己shift來跳到下一個位置, getopts只需要在最後使用shift $(($OPTIND - 1))來跳到parameter的位置。 4. 使用getopt時, 在命令列輸入的位置引數是什麼, 在getopt中需要保持原樣, 比如 -t , 在getopt的case語句中也要使用-t,  而getopts中不要前面的-。 5. getopt往往需要跟set配合使用 6. getopt -o的選項注意一下

7. getopts 使用語法簡單,getopt 使用語法較複雜

8. getopts 不會重排所有引數的順序,getopt 會重排引數順序

9. getopts 出現的目的是為了代替 getopt 較快捷的執行引數分析工作

下面是getopt自帶的一個例子:

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->
#!/bin/bash

# A small example program for using the new getopt(1) program.
# This program will only work with bash(1)
# An similar program using the tcsh(1) script language can be found
# as parse.tcsh

# Example input and output (from the bash prompt):
# ./parse.bash -a par1 'another arg' --c-long 'wow!*?' -cmore -b " very long "
# Option a
# Option c, no argument
# Option c, argument `more'
# Option b, argument ` very long '
# Remaining arguments:
# --> `par1'
# --> `another arg'
# --> `wow!*?'

# Note that we use `"$@"' to let each command-line parameter expand to a
# separate word. The quotes around `$@' are essential!
# We need TEMP as the `eval set --' would nuke the return value of getopt.

#-o表示短選項,兩個冒號表示該選項有一個可選引數,可選引數必須緊貼選項
#		如-carg 而不能是-c arg
#--long表示長選項
#"$@" :引數本身的列表,也不包括命令本身
# -n:出錯時的資訊
# -- :舉一個例子比較好理解:
#我們要建立一個名字為 "-f"的目錄你會怎麼辦?
# mkdir -f #不成功,因為-f會被mkdir當作選項來解析,這時就可以使用
# mkdir -- -f 這樣-f就不會被作為選項。

TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: 
     -n 'example.bash' -- "$@"`

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

# Note the quotes around `$TEMP': they are essential!
#set 會重新排列引數的順序,也就是改變$1,$2...$n的值,這些值在getopt中重新排列過了
eval set -- "$TEMP"

#經過getopt的處理,下面處理具體選項。

while true ; do
    case "$1" in
        -a|--a-long) echo "Option a" ; shift ;;
        -b|--b-long) echo "Option b, argument `$2'" ; shift 2 ;;
        -c|--c-long)
            # c has an optional argument. As we are in quoted mode,
            # an empty parameter will be generated if its optional
            # argument is not found.
            case "$2" in
                "") echo "Option c, no argument"; shift 2 ;;
                *)  echo "Option c, argument `$2'" ; shift 2 ;;
            esac ;;
        --) shift ; break ;;
        *) echo "Internal error!" ; exit 1 ;;
    esac
done
echo "Remaining arguments:"
for arg do
   echo '--> '"`$arg'" ;
done

關於 getopt 選項重排的解釋:

比如我們使用 ./test -a  -b arg arg1 -c  你可以看到,命令列中多了個arg1引數,在經過getopt和set之後,命令列會變為: -a -b arg -c -- arg1 $1指向-a,$2指向-b,$3指向arg,$4指向-c,$5指向--,而多出的arg1則被放到了最後。

getopt 對引數順序進行重排的意義:這樣可以將帶 "-" 或 "–" 的引數寫在其他引數的前面,也可以寫在後面,而 getopts 是沒有這樣的能力的,具體沒有的原因就是因為 getopts 直接進入了 while 迴圈處理引數,而 getopt 遊一個 set — ${ARGS} 的過程。另外還要注意到的是,在使用 getopt 處理完引數之後,"${@}" 變數 “被清洗乾淨了” ,裡面包含了所有不帶 "-" 或 "–" 的引數,所以你可以繼續使用 ${1},${2} 等來呼叫他們。

3、Refer:

1、Bash Shell中命令列選項/引數處理

http://www.cnblogs.com/FrankTan/archive/2010/03/01/1634516.html

2、bash處理命令列引數:getopts/getopt 

http://blog.chinaunix.net/uid-21651880-id-3392466.html

3、getopt 使用教程並與 getopts 比較

http://hiaero.net/getopts-versus-getopt/

4、bash getopts, short options only, all require values, own validation

http://unix.stackexchange.com/questions/75219/bash-getopts-short-options-only-all-require-values-own-validation