1. 程式人生 > >Shell指令碼中引數處理方法

Shell指令碼中引數處理方法

'getopt'與'getopts'類似,不過'getopts'只能處理短選項,'getopt'則能處理短選項和長選項。所謂的短選項就是類似下面這樣的選項:

-a

而下面這樣的則是長選項

--action=delete

當然,事無絕對,通過一些技巧,用'getopts'處理長選項也是可能的。這裡先說一下如何用'getopt'來處理引數吧。

需要事先說明的一點是,'getopt'不是Shell內建的命令,而是'util-linux'這個軟體包提供的功能,它不是POSIX標準的一部分,所以也有人建議不使用'getopt'

首先將之前說到的五種動作對應的短選項擴充套件一下,以便講解'getopt'的使用:

  1. -d/–delete : 將檔案移動到回收站,該選項後需要指定一個檔案或目錄名
  2. -l/–list : 列出被移動到回收站的檔案及其id,該選項不需要值
  3. -b/–back : 恢復被移動到回收站的檔案,該選項需要指定一個檔案對應的id
  4. -c/–clear : 清空回收站,該選項不需要值
  5. -h/–help : 列印幫助資訊

'getopt'既能處理短選項也能處理長選項,短選項通過引數-o指定,長選項通過引數-l指定。同'getopts'一樣,它一次也只解析一個選項,所以也需要迴圈處理,不過與'getopts'不同的是,'getopt'沒有使用OPTINDOPTARG這兩個變數,所以我們還得手動對引數進行'shift',對需要值的選項,也得手動去取出值。

下面是在Shell中使用'getopt'的一個示例:

可以看到,'getopt'將引數中以下形式的內容:

--longopt=argument

在返回結果中替換成下面這樣的形式:

--longopt argument

這樣就可以通過迴圈和'shift'來進行處理了,不過在指令碼中,'shift'命令是對命令列引數起作用的,即特殊變數"[email protected]",而我們在指令碼中只能將'getopt'的返回結果作為字串儲存到一個變數中。為了讓'shift'起作用,通常還要使用'set'命令來將變數的值賦給"[email protected]"這個特殊變數。

真是有夠麻煩的……算了,下面再集中吐槽吧……

然後,在設定好短選項和長選項後,在將實際的引數傳給'getopt'時,要在實際引數前加上一個兩個連字元--,而'getopt'會將這兩個連字元放到返回結果的最後面,在處理時可以將這兩個連字元視為結束標誌。

以下是針對本文假設的情景,使用'getopt'解析引數的流程:

arg=$(getopt -o d:lb:ch -l delete:,list,back:,clear,help -- $@)

set -- "$arg"

while true
do
    case $1 in
        -d|--delete)
            file_to_trash=$2
            trash $file_to_trash # trash is a function
            shift 2
            ;;
        -l|--list)
            print_trashed_file  # print_trashed_file is a function
            shift
            ;;
        -b|--back)
            file_to_untrash=$2
            untrash $file_to_untrash # untrash is a function
            shift
            ;;
        -c|--clear)
            clean_all           # clean all is a function
            shift
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        --)
            shift
            break
            ;;
    esacdone

然而,知道了'getopt'的使用及其原理後,自然而然地可以發現,我可以不用去管這個結束標誌,用"$#"這個表示引數個數的特殊變數,同樣可以控制引數解析的流程,這完全和手工解析是同一個道理。我甚至可以將'getopt'的返回結果儲存到一個數組裡,直接迴圈處理這個陣列,而不用使用'set'命令了。

好了,吐槽時間。

我之前寫指令碼都是用的'getopts',一來我用不上長選項,二來'getopts'的使用足夠簡單。在寫本文之前,我倒是知道'getopt'可以處理長選項,但沒仔細瞭解過。這兩天瞭解了一下,覺得還是別用'getopt'的好,理由如下:

  1. 'getopt'不是Shell內建命令,跨平臺使用時可能會出現問題;
  2. 只是將'–longopt=val'這樣的引數形式替換成了'–longopt val',但因此增加了許多複雜性,比如使用了'set'命令,在使用'set'命令時還要考慮'getopt'的返回結果中有無Shell命令,有的話應該使用'eval'命令來消除可能導致的錯誤
    eval set -- "$arg"
    
  3. 呼叫完還要進行與手工解析類似的工作,相比手工解析,並沒有多大優勢;
  4. 真的需要長選項嗎?我覺得短選項就足夠了