getopts可以解析bash脚本的子集';让我们来谈谈你的论点,剩下的就完好无损了?

getopts可以解析bash脚本的子集';让我们来谈谈你的论点,剩下的就完好无损了?,bash,getopts,Bash,Getopts,我正在使用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 ...` 我需要做两件

我正在使用
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
选项之间使用
-
来解决我的问题,但是如果
-
后面的文本以
-
开头,则
getopts
似乎会失败(我尝试了,但无法摆脱连字符)

无论如何,我不想使用
--
,因为我希望
条的存在对
foo
的调用方有效透明,并且我希望
foo
的调用方能够以任何顺序呈现选项

我还尝试在
foo
中列出所有
bar
选项(即对optstring使用
'a:b:cdef:'
),但没有处理它们,但我需要在发生时从
“$@”
中删除处理过的选项。我不知道怎么做(
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[@]}" "$@"
所以

将调用
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[@]}"
注意事项:有两种类型的歧义无法通过这种方式解决:

#!/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[@]}"
  • 对于带有选项参数的传递选项,此方法仅在参数未直接附加到选项时有效。
    例如,
    -aval\u a
    有效,但
    -aval\u a
    无效(在
    getopts
    参数中没有
    a:
    ,这将被解释为一个选项组,并将其转换为多个选项
    -a
    -v
    -a
    -l
    -a

  • 正如在一篇关于这个问题的评论中指出的,
    -a-b
    可以是带有选项参数
    -b
    的选项
    -a
    (这恰好看起来像一个选项本身),也可以是不同的选项
    -a
    -b
    ;上述方法将实现后者


要解决这些含糊不清的问题,您必须坚持,这有一个缺点,就是需要事先了解所有可能的传递选项。

一个(似乎无法克服)问题:如果您看到
-a-b3
,是一个未知选项
-a
,没有参数,后面跟着预期的
-b
选项,或者它是一个未知的选项
-a
,带有一个参数
-b
,后跟一个位置参数
3
?@chepner:这一点很好。另一个问题是:
-ab
选项
-a
带有选项参数
b
,或者一个选项组被解释为
-a
-b
?这看起来更难以克服:),除了通配符的情况外,与我所拥有的类似。我认为这些警告是相当模糊的,可以合理忽略,以获得不需要事先了解传递参数的好处。我不知道你能改变getopts变量。很好的解决方案。