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;上面的方法会做后者。

要解决这些歧义,您必须坚持 ,其缺点是需要提前了解所有可能的直通选项。