我有一个 shell 脚本,它接受各种选项,有些带有参数,有些没有,有些短,有些长。它应该自己处理其中一些选项,并将其不知道如何关心的其余选项传递给另一个程序。我可以得到getopts
类似的东西来存储未知的参数吗?
例如,假设我的脚本被调用program
并且应该接受参数
-n
用一个参数,--verbose
没有任何争论并且-s
没有任何争论。
它解析并打印所有选项及其参数,然后echo rest:
使用剩余的任何内容进行调用。应观察以下输出。
> program -sin42 --long --verbose
-s
-n with argument 42
--verbose
rest: -i --long
> program -n --short
-n with argument --short
> program -n
error: -n without argument
这样的事情可以在shell脚本中实现吗?
答案1
我不认为有一个可用的标准方法(即除了从头开始实现它之外),更不用说在 shell 中广泛使用了。
虽然ksh
支持相当强大的getopts
内置功能。基于此(以及您相当苛刻的要求),我ksh
使用以下代码片段概述了一个可能的基础解决方案:
while getopts ":[-][[n]:][99:verbose][s]" opt
do case $opt in
(n) n_arg=$OPTARG ;;
(99) verbose=1 ;;
(s) s=1 ;;
(*) arg_rest+=( "${@:OPTIND-1:1}" ) ;;
esac
done
shift OPTIND-1
printf "main opt(%s)=%s\n" "-n" "$n_arg"
printf "main opt(%s)=%s\n" "--verbose" "$verbose"
printf "main opt(%s)=%s\n" "-s" "$s"
function delegate
{
while getopts ":[-][i][98:long]" opt
do case $opt in
(i) int=1 ;;
(98) long=1 ;;
esac
done
shift OPTIND-1
printf "func opt(%s)=%s\n" "-i" "$int"
printf "func opt(%s)=%s\n" "--long" "$long"
}
printf "Delegate: '%s'\n" "${arg_rest[@]}"
delegate "${arg_rest[@]}"
程序首先解析所有选项,根据需要设置内部变量,并将未知选项存储在数组中。然后你会看到一些printf
控制设置的选项。然后是一个函数定义,其余选项将被委托给该函数;该函数也可以用命令代替。最后使用其余参数调用函数(或其他命令)。
ksh
(有关功能的描述,getopts
请从ksh
会话中调用getopts --man
。)
运行该程序会产生以下输出:
$ ksh ./getopts_script -s -n 23 --verbose -i --long
main opt(-n)=23
main opt(--verbose)=1
main opt(-s)=1
Delegate: '-i'
Delegate: '--long'
func opt(-i)=1
func opt(--long)=1
有关支持长选项的 getopts 函数的 shell 实现,请参阅https://github.com/stephane-chazelas/misc-scripts/blob/master/getopts_long.sh
答案2
人们很容易尝试getopt
预先订购已知的选项。不幸的是,它拒绝所有未知参数并立即停止解析。
但是,只要长选项不是过于复杂,bashgetopts
就可以被欺骗来执行此操作。一些灵感来自于mkaurball
。来源下方附有注意事项。
#!/bin/bash
while [ $OPTIND -le $# ]
do
if getopts ":sn:-:" argument
then
case $argument in
s) echo "-$argument" ;;
n) echo "-$argument with argument $OPTARG" ;;
\?) pass+=("-$OPTARG") ;;
-) lastarg=$((OPTIND - 1))
case "${!lastarg}" in
--verbose) echo "--verbose" ;;
--*) pass+=("--$OPTARG") ;;
*) echo "invalid argument: -"
exit 1 ;;
esac ;;
:) echo "$OPTARG without argument"
exit 1 ;;
esac
else
pass+=("${!OPTIND}")
let OPTIND++
fi
done
echo pass: "${pass[@]}"
- 不同寻常的是,
getopts
s 不用作 的条件while
,这会导致循环在遇到任何位置参数(即既不是选项也不是参数的任何参数)时立即停止。相反,这些情况会附加到收集参数的数组中pass
。 - 参数规范包括
:-
,这使得getopts
将长选项视为带有参数的--long
短选项。如果不小心使用,将破折号混合到短选项复合词(例如 )的中间可能会破坏这一点,因此最好检查实际参数是否确实以两个破折号开头。因此,如果包装的程序想要将选项视为选项,则该方法将会失败。-
-long
-s-n
-short-with-dashes
- 尽管就 的本质而言
getopts
,双破折号也会被吞掉。这可能是想要的,但也可以通过在[ "${!OPTIND}" != '--' ] &&
之前插入来避免getopts
- 包装器解析长参数可能有些困难,因为它们必须在嵌套的
case
.