问题描述
我想用getopts构建一个脚本,当未设置$ OPTARG时,该脚本在标志中继续。 我的脚本如下:
OPTIONS=':dBhmtb:P:'
while getopts $OPTIONS OPTION
do
case "$OPTION" in
m ) echo "m"
t ) echo "t"
d ) echo "d";;
h ) echo "h";;
B ) echo "b";;
r ) echo "r";;
b ) echo "b"
P ) echo hi;;
#continue here
\? ) echo "?";;
:) echo "test -$OPTARG requieres an argument" >&2
esac
done
当没有为-P设置$ OPTARG时,我的目的是继续我的评论。
运行./test -P后,我得到的是:
test -P requieres an argument
然后它在循环后继续,但是我想在-P标志中继续。
全清?
有任何想法吗?
解决方法
首先,修复某些案例分支中缺少的;;
。
我认为您不能:您告诉getopts -P
需要一个参数:两个错误情况
-
-P
(不带参数)是 last 选项。在这种情况下,getops看到-P之后没有任何内容,并将OPTION变量设置为:
,您可以在case语句中进行处理。 -
-P
之后是另一个选项:即使下一个单词是另一个选项,getopts也将简单地接受下一个单词,例如OPTARG。将案例分支更改为
P ) echo "P: '$OPTARG'";;
然后:
- 调用
bash script.sh -P -m -t
之类的脚本,输出为P: '-m' t
- 调用
bash script.sh -Pmt
之类的脚本,输出为P: 'mt'
这显然很难解决。您怎么知道用户是否打算将选项参数实际上是“ mt”而不是选项-m和-t?
- 调用
您也许可以使用getopt
(请参阅the canonical example)来解决此问题,为长选项(那些需要与--long=value
等号的可选参数)使用可选参数检查选项参数是否丢失。
将getopts
解析为getopt
-比较冗长,但是您拥有更细粒度的控制
die() { echo "$*" >&2; exit 1; }
tmpArgs=$(getopt -o 'dBhmt' \
--long 'b::,P::' \
-n "$(basename "$0")" \
-- "$@"
)
(( $? == 0 )) || die 'Problem parsing options'
eval set -- "$tmpArgs"
while true; do
case "$1" in
-d) echo d; shift ;;
-B) echo B; shift ;;
-h) echo h; shift ;;
-m) echo m; shift ;;
-t) echo t; shift ;;
--P) case "$2" in
'') echo "P with no argument" ;;
*) echo "P: $2" ;;
esac
shift 2
;;
--b) case "$2" in
'') echo "b with no argument" ;;
*) echo "b: $2" ;;
esac
shift 2
;;
--) shift; break ;;
*) printf "> %q\n" "$@"
die 'getopt internal error: $*' ;;
esac
done
echo "Remaining arguments:"
for ((i=1; i<=$#; i++)); do
echo "$i: ${!i}"
done
通过--P
成功调用程序:
$ ./myscript.sh --P -mt foo bar
P with no argument
m
t
Remaining arguments:
1: foo
2: bar
$ ./myscript.sh --P=arg -mt foo bar
P: arg
m
t
Remaining arguments:
1: foo
2: bar
这确实会给您的用户带来更高的开销,因为-P
(带短划线)无效,并且参数必须以=
给出
$ ./myscript.sh --P arg -mt foo bar
P with no argument
m
t
Remaining arguments:
1: arg
2: foo
3: bar
$ ./myscript.sh --Parg mt foo bar
myscript.sh: unrecognized option `--Parg'
Problem parsing options
$ ./myscript.sh -P -mt foo bar
myscript.sh: invalid option -- P
Problem parsing options
$ ./myscript.sh -P=arg -mt foo bar
myscript.sh: invalid option -- P
myscript.sh: invalid option -- =
myscript.sh: invalid option -- a
myscript.sh: invalid option -- r
myscript.sh: invalid option -- g
Problem parsing options
,
- 请勿将逻辑与参数解析混在一起。
- 首选小写变量。
当没有为-P设置$ OPTARG时,我的目的是继续我的评论
我建议不要这样做。您在一个范围内所做的事情越少,您所考虑的就越少。将解析选项和执行操作分开在不同的阶段。我建议:
# set default values for options
do_something_related_to_P=false
recursive=false
tree_output=false
# parse arguments
while getopts ':dBhmtb:P:' option; do
case "$option" in
t) tree_output=true; ;;
r) recursive="$OPTARG"; ;;
P) do_something_related_to_P="$OPTARG"; ;;
\?) echo "?";;
:) echo "test -$OPTARG requieres an argument" >&2
esac
done
# application logic
if "$do_something_related_to_P"; then
do something related to P
if "$recursive"; then
do it in recursive style
fi
fi |
if "$tree_output"; then
output_as_tree
else
cat
fi
,
“不要在大小写分支中放置 programming 应用程序逻辑”的示例-touch
命令可以使用-t timespec
选项或-r referenceFile
选项,但不能同时使用:
$ touch -t 202010100000 -r file1 file2
touch: cannot specify times from more than one source
Try 'touch --help' for more information.
我会这样实现(忽略其他选项):
while getopts t:r: opt; do
case $opt in
t) timeSpec=$OPTARG ;;
r) refFile=$OPTARG ;;
esac
done
shift $((OPTIND-1))
if [[ -n $timeSpec && -n $refFile ]]; then
echo "touch: cannot specify times from more than one source" >&2
exit 1
fi
我会不这样做:
while getopts t:r: opt; do
case $opt in
t) if [[ -n $refFile ]]; then
echo "touch: cannot specify times from more than one source" >&2
exit 1
fi
timeSpec=$OPTARG ;;
r) if [[ -n $timeSpec ]]; then
echo "touch: cannot specify times from more than one source" >&2
exit 1
fi
refFile=$OPTARG ;;
esac
done
您可以看到逻辑是否变得更加复杂(正如我所提到的,恰好是-a或-b或-c之一),所以case语句的大小很容易就无法维持。