当 Bash、GETOPTS 中没有设置 $OPTARG 时,有没有办法在 Flag 中继续?
Is there a way to continue in a Flag when there is no $OPTARG set in Bash, GETOPTS?
我想用 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
-- 它更冗长,但您有 finer-grained 控制权
die() { echo "$*" >&2; exit 1; }
tmpArgs=$(getopt -o 'dBhmt' \
--long 'b::,P::' \
-n "$(basename "[=13=]")" \
-- "$@"
)
(( $? == 0 )) || die 'Problem parsing options'
eval set -- "$tmpArgs"
while true; do
case "" in
-d) echo d; shift ;;
-B) echo B; shift ;;
-h) echo h; shift ;;
-m) echo m; shift ;;
-t) echo t; shift ;;
--P) case "" in
'') echo "P with no argument" ;;
*) echo "P: " ;;
esac
shift 2
;;
--b) case "" in
'') echo "b with no argument" ;;
*) echo "b: " ;;
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
- 不要将逻辑与参数解析混在一起。
- 首选小写变量。
My aim is to continue at my comment, when there is no $OPTARG set for -P
我建议不要。你在一个范围内做的越少,你需要考虑的就越少。在不同的阶段拆分解析选项和执行操作。我建议:
# 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
“不要将 编程 应用程序逻辑放在案例分支中”的示例 -- 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 语句的大小很容易膨胀到无法维护。
我想用 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
-- 它更冗长,但您有 finer-grained 控制权
die() { echo "$*" >&2; exit 1; }
tmpArgs=$(getopt -o 'dBhmt' \
--long 'b::,P::' \
-n "$(basename "[=13=]")" \
-- "$@"
)
(( $? == 0 )) || die 'Problem parsing options'
eval set -- "$tmpArgs"
while true; do
case "" in
-d) echo d; shift ;;
-B) echo B; shift ;;
-h) echo h; shift ;;
-m) echo m; shift ;;
-t) echo t; shift ;;
--P) case "" in
'') echo "P with no argument" ;;
*) echo "P: " ;;
esac
shift 2
;;
--b) case "" in
'') echo "b with no argument" ;;
*) echo "b: " ;;
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
- 不要将逻辑与参数解析混在一起。
- 首选小写变量。
My aim is to continue at my comment, when there is no $OPTARG set for -P
我建议不要。你在一个范围内做的越少,你需要考虑的就越少。在不同的阶段拆分解析选项和执行操作。我建议:
# 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
“不要将 编程 应用程序逻辑放在案例分支中”的示例 -- 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 语句的大小很容易膨胀到无法维护。