我有一个当前与位置参数一起使用的脚本,如下所示:
./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
但在这里,我需要使用正则表达式来检查选项,这可能不可靠。
是否有一种结合命令行选项和位置参数的本机方法?
答案1
使用 实现普通命令行解析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
是选项。)
答案2
那么你为什么要使用它if
呢?只需检查参数即可。
flaggiven=0
# This is very unsafe - use `getopt` or `getopts`. `shift` can fail - should be checked for errors.
while (($#)); do
case "$1" in
-f) fname=$2; flaggiven=1; shift; ;;
-l) lname=$2; flaggiven=1; shift; ;;
-a) address=$2; flaggiven=1; shift; ;;
*) break;
esac
shift
done
if (($# > 0)); then
if ((flaggiven)); then
echo "ERROR: you have given -f or -l or -a argument and arguments. One or the other, not both!"
exit 1
fi
fname=$1
lname=$2
address=$3
else
if ((!flaggiven)); then
echo "ERROR: you have to pass -f -l -a flags or pass 3 arguments"
exit 1
fi
fi