Bash
如何在為使用位置參數而編寫的腳本中引入對命令行選項的支持?
我有一個目前與位置參數一起使用的腳本,如下所示:
./script.sh fname lname address
當我呼叫這個腳本時,我還想支持普通的命令行選項,這樣我就可以跳過我不想給出的參數:
./script.sh -f fname -a address
fname
是唯一的強制參數。由於歷史原因和自動化,腳本需要向後兼容。
到目前為止,我最好的猜測是尋找
-f
字元串(被空格包圍),
- 如果找到,處理標誌
- 如果不是,按順序處理
flags='(\ |^)-f\ ' if [[ $* =~ $flags ]]; then while [ $# -ne 0 ] do name="$1" case "$name" in -f) shift fname=$1 ;; -l) shift lname=$1 ;; -a) shift address=$1 ;; esac shift done else fname=${1} lname=${2} address=${3} fi
但是在這裡,我需要使用正則表達式來檢查選項,這可能是不可靠的。
有沒有一種結合命令行選項和位置參數的本地方法?
使用
getopts
. 如果該解析被觸發,設置一個標誌來跟踪這個事實。在標準getopts
循環之後,如果尚未設置標誌,則回退到舊行為。在下面的程式碼中,標誌是變數
new_behavior
。#!/bin/bash # As written now, this should run without issues with # /bin/sh too (there are no bash-isms in this code). unset address unset fname unset lname new_behavior=false while getopts 'a:f:l:' opt; do new_behavior=true case $opt in a) address=$OPTARG ;; f) fname=$OPTARG ;; l) lname=$OPTARG ;; *) echo 'Error in command line parsing' >&2 exit 1 esac done shift "$(( OPTIND - 1 ))" if ! "$new_behavior"; then # Fall back on old behavior. fname=$1; shift lname=$1; shift address=$1; shift fi if [ -z "$fname" ]; then echo 'Missing mandatory argument "fname"' >&2 exit 1 fi # The rest is unimportant to the actual command line parsing code # and only here for your information and for debugging. printf 'fname="%s", lname="%s", address="%s"\n' \ "$fname" "$lname" "$address" if [ "$#" -gt 0 ]; then printf 'Extra argument: "%s"\n' "$@" fi
只要使用者提供選項作為腳本的第一個參數,就會觸發新行為。選項是否有效並不重要。
測試舊行為:
$ ./script eff ell addr fname="eff", lname="ell", address="addr"
$ ./script eff ell addr "hello world" fname="eff", lname="ell", address="addr" Extra argument: "hello world"
$ ./script eff fname="eff", lname="", address=""
$ ./script Missing mandatory argument "fname"
$ ./script eff -l ell fname="eff", lname="-l", address="ell"
測試新行為:
$ ./script -a addr -l ell -f eff fname="eff", lname="ell", address="addr"
$ ./script -a addr -f eff "hello world" fname="eff", lname="", address="addr" Extra argument: "hello world"
$ ./script -f eff "hello world" fname="eff", lname="", address="" Extra argument: "hello world"
$ ./script -l eff "hello world" Missing mandatory argument "fname"
$ ./script -f eff -- -l ell -f "eff again" fname="eff", lname="", address="" Extra argument: "-l" Extra argument: "ell" Extra argument: "-f" Extra argument: "eff again"
(請注意,在最後一個範例中,非選項額外參數與帶有 的選項分隔,第二個和第二個
--
都不是選項。)-l``-f