【问题标题】:Can getopts parse a subset of a bash script's arguments and leave the rest intact?getopts 可以解析 bash 脚本参数的子集并保持其余部分不变吗?
【发布时间】:2016-10-17 14:01:43
【问题描述】:

我正在使用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 似乎会出现问题(我试过但无法逃脱连字符)。

无论如何,我宁愿不必使用--,因为我希望bar 的存在对foo 的调用者有效,并且我希望foo 的调用者能够以任何顺序显示选项。

我还尝试在foo 中列出所有baroptions(即使用'a:b:cdef:'作为optstring)而不处理它们,但我需要从"$@" 中删除已处理的选项,因为它们发生。我不知道该怎么做(shift 不允许指定位置)。


我可以手动重建一个新的选项列表(请参阅我自己的答案),但我想知道是否有更好的方法来做到这一点。

【问题讨论】:

  • 一个(看似无法克服的)问题:如果你看到-a -b 3,那是一个未知选项-a,没有参数,后跟预期的-b 选项,还是未知选项@987654354 @ 带有参数 -b 后跟位置参数 3?
  • @chepner:好点。另一个问题:是-ab 选项-a 带有选项参数b,还是一个选项组被解释为-a-b
  • 这看起来更不可逾越:)

标签: bash getopts


【解决方案1】:

尝试以下操作,只需要提前知道脚本的自己的选项

#!/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[@]}"

注意事项:有两种类型的无法通过这种方式解决的歧义

  • 对于传递选项带有选项参数,此方法仅在参数未直接附加到选项时才有效。
    例如,-a val_a 有效,但 -aval_a 无效(在 getopts 参数中没有 a: 时,这将被解释为选项 group 并将其转换为多个选项-a-v-a-l-_-a)。

  • 正如chepner 在对该问题的评论中指出的那样,-a -b 可以是选项 -a 和选项参数 -b(恰好看起来像一个选项本身),或者它可以是不同的选项-a-b;上述方法将做后者。

要解决这些歧义,您必须使用 with your own approach,它的缺点是需要提前了解所有可能的直通选项。

【讨论】:

  • 与我所拥有的类似,除了通配符大小写。我认为这些警告非常模糊并且可以合理地忽略,以获得不需要事先了解传递参数的好处。我不明白您可以更改 getopts 变量。很好的解决方案。
【解决方案2】:

您可以像此示例一样手动重建选项列表,该示例处理 -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[@]}" "$@"

这将使"$@" 包含问题中指定的未处理参数。

【讨论】:

    猜你喜欢
    • 2019-02-04
    • 1970-01-01
    • 2021-06-09
    • 2011-01-21
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    • 2021-04-29
    • 2021-12-03
    相关资源
    最近更新 更多