Bash

如何在為使用位置參數而編寫的腳本中引入對命令行選項的支持?

  • July 26, 2021

我有一個目前與位置參數一起使用的腳本,如下所示:

./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

引用自:https://unix.stackexchange.com/questions/659977