getopts 能否解析 bash 脚本参数的一个子集并保持其余部分不变?
Can getopts parse a subset of a bash script's arguments and leave the rest intact?
我正在使用 getopts
来解析 bash
脚本中的参数。我想做两件事:
- 从
"$@"
中删除已处理的选项
- 在
"$@"
中保留未处理的选项
考虑命令行
$ foo -a val_a -b val_b -c -d -e -f val_f positional_l positional_2 ...
其中 foo
使用 getopts
解析由 'b:c'
的 optstring 定义的选项,之后需要离开 "$@"
作为
`-a val_a -d -e -f val_f positional_l positional_2 ...`
我需要做两件事:
- 解析可能给出的选项子集
- 保留所有其他选项不变
这是因为 foo
必须使用它识别的选项来确定另一个脚本 bar
,它必须将剩余的 "@"
.
传递给该脚本
通常 getopts
在遇到无法识别的选项时停止,但我需要它继续(最多 --
)。我需要它来处理和删除它识别的选项,并保留它不识别的选项。
我确实尝试在 foo
选项和 bar
选项之间使用 --
来解决我的问题,但是如果 --
以 -
开头(我试过但无法转义连字符)。
无论如何,我宁愿不必使用 --
,因为我希望 bar
的存在对 foo
的调用者有效透明,并且我希望foo
的调用者能够以任何顺序显示选项。
我还尝试在 foo
中列出所有 bar
选项(即使用 'a:b:cdef:'
作为 optstring)而不处理它们,但我需要从 "$@"
中删除已处理的。我不知道该怎么做(shift
不允许指定位置)。
我可以手动重建一个新的选项列表(参见我自己的回答),但我想知道是否有更好的方法来做到这一点。
您可以手动重建选项列表,例如处理 -b
和 -c
选项并传递任何完好无损的选项。
#!/bin/bash
while getopts ":a:b:cdef:" opt
do
case "${opt}" in
b) file="$OPTARG" ;;
c) ;;
*) opts+=("-${opt}"); [[ -n "$OPTARG" ]] && opts+=("$OPTARG") ;;
esac
done
shift "$((OPTIND-1))"
./$file "${opts[@]}" "$@"
所以
./foo -a 'foo bar' -b bar -c -d -e -f baz one two 'three and four' five
将调用 bar
,作为选项 b
的参数给出,如
./bar -a 'foo bar' -d -e -f baz one two 'three and four' five
此解决方案的缺点是 optstring 必须包含传递选项(即 ":a:b:cdef:"
而不是更可取的 ":b:c"
)。
可以这样用重构的参数列表替换参数列表:
set -- "${opts[@]}" "$@"
这将使 "$@"
包含问题中指定的未处理参数。
尝试以下方法,只需要提前知道脚本的自己的选项:
#!/usr/bin/env bash
passThru=() # init. pass-through array
while getopts ':cb:' opt; do # look only for *own* options
case "$opt" in
b)
file="$OPTARG";;
c) ;;
*) # pass-thru option, possibly followed by an argument
passThru+=( "-$OPTARG" ) # add to pass-through array
# see if the next arg is an option, and, if not,
# add it to the pass-through array and skip it
if [[ ${@: OPTIND:1} != -* ]]; then
passThru+=( "${@: OPTIND:1}" )
(( ++OPTIND ))
fi
;;
esac
done
shift $((OPTIND - 1))
passThru+=( "$@" ) # append remaining args. (operands), if any
./"$file" "${passThru[@]}"
注意事项:有两种类型的歧义无法通过这种方式解决:
对于带有选项参数的直通选项,此方法仅在参数不是直接[=49=时才有效] 附加到选项。
例如,-a val_a
有效,但 -aval_a
无效(在 getopts
参数中没有 a:
的情况下,这将被解释为选项 group 变成多个选项 -a
, -v
, -a
, -l
, -_
, -a
)。
正如 chepner 在对该问题的评论中指出的那样,-a -b
可以是带有选项参数 -b
的选项 -a
(即恰好看起来像一个选项本身),或者它可能是不同的选项 -a
和 -b
;上面的方法会做后者。
要解决这些歧义,您必须坚持 ,其缺点是需要提前了解所有可能的直通选项。
我正在使用 getopts
来解析 bash
脚本中的参数。我想做两件事:
- 从
"$@"
中删除已处理的选项
- 在
"$@"
中保留未处理的选项
考虑命令行
$ foo -a val_a -b val_b -c -d -e -f val_f positional_l positional_2 ...
其中 foo
使用 getopts
解析由 'b:c'
的 optstring 定义的选项,之后需要离开 "$@"
作为
`-a val_a -d -e -f val_f positional_l positional_2 ...`
我需要做两件事:
- 解析可能给出的选项子集
- 保留所有其他选项不变
这是因为 foo
必须使用它识别的选项来确定另一个脚本 bar
,它必须将剩余的 "@"
.
通常 getopts
在遇到无法识别的选项时停止,但我需要它继续(最多 --
)。我需要它来处理和删除它识别的选项,并保留它不识别的选项。
我确实尝试在 foo
选项和 bar
选项之间使用 --
来解决我的问题,但是如果 --
以 -
开头(我试过但无法转义连字符)。
无论如何,我宁愿不必使用 --
,因为我希望 bar
的存在对 foo
的调用者有效透明,并且我希望foo
的调用者能够以任何顺序显示选项。
我还尝试在 foo
中列出所有 bar
选项(即使用 'a:b:cdef:'
作为 optstring)而不处理它们,但我需要从 "$@"
中删除已处理的。我不知道该怎么做(shift
不允许指定位置)。
我可以手动重建一个新的选项列表(参见我自己的回答),但我想知道是否有更好的方法来做到这一点。
您可以手动重建选项列表,例如处理 -b
和 -c
选项并传递任何完好无损的选项。
#!/bin/bash
while getopts ":a:b:cdef:" opt
do
case "${opt}" in
b) file="$OPTARG" ;;
c) ;;
*) opts+=("-${opt}"); [[ -n "$OPTARG" ]] && opts+=("$OPTARG") ;;
esac
done
shift "$((OPTIND-1))"
./$file "${opts[@]}" "$@"
所以
./foo -a 'foo bar' -b bar -c -d -e -f baz one two 'three and four' five
将调用 bar
,作为选项 b
的参数给出,如
./bar -a 'foo bar' -d -e -f baz one two 'three and four' five
此解决方案的缺点是 optstring 必须包含传递选项(即 ":a:b:cdef:"
而不是更可取的 ":b:c"
)。
可以这样用重构的参数列表替换参数列表:
set -- "${opts[@]}" "$@"
这将使 "$@"
包含问题中指定的未处理参数。
尝试以下方法,只需要提前知道脚本的自己的选项:
#!/usr/bin/env bash
passThru=() # init. pass-through array
while getopts ':cb:' opt; do # look only for *own* options
case "$opt" in
b)
file="$OPTARG";;
c) ;;
*) # pass-thru option, possibly followed by an argument
passThru+=( "-$OPTARG" ) # add to pass-through array
# see if the next arg is an option, and, if not,
# add it to the pass-through array and skip it
if [[ ${@: OPTIND:1} != -* ]]; then
passThru+=( "${@: OPTIND:1}" )
(( ++OPTIND ))
fi
;;
esac
done
shift $((OPTIND - 1))
passThru+=( "$@" ) # append remaining args. (operands), if any
./"$file" "${passThru[@]}"
注意事项:有两种类型的歧义无法通过这种方式解决:
对于带有选项参数的直通选项,此方法仅在参数不是直接[=49=时才有效] 附加到选项。
例如,-a val_a
有效,但-aval_a
无效(在getopts
参数中没有a:
的情况下,这将被解释为选项 group 变成多个选项-a
,-v
,-a
,-l
,-_
,-a
)。正如 chepner 在对该问题的评论中指出的那样,
-a -b
可以是带有选项参数-b
的选项-a
(即恰好看起来像一个选项本身),或者它可能是不同的选项-a
和-b
;上面的方法会做后者。
要解决这些歧义,您必须坚持