【问题标题】:How to wrap another shell still passing $OPTIND as-is?如何包装另一个仍然通过 $OPTIND 的外壳?
【发布时间】:2023-03-03 13:26:01
【问题描述】:

我正在尝试用脚本 a 包装 bash 脚本 b。 但是,我想将传递给 a 的选项也传递给 b 。

#!/bin/bash

# script a
./b ${@:$OPTIND}

这也将打印 $1(如果有)。最简单的方法是什么?

呼唤:

./a -c -d 5 first-arg

我希望 b 执行: ./b -c -d 5 # 没有第一个参数

【问题讨论】:

    标签: bash scripting code-reuse getopts


    【解决方案1】:

    在 bash 中,您可以构建一个包含选项的数组,并使用该数组来调用辅助程序。

    call_b () {
      typeset -i i=0
      typeset -a a; a=()
      while ((++i <= OPTIND)); do   # for i=1..$OPTIND
        a+=("${!i}")                # append parameter $i to $a
      done
      ./b "${a[@]}"
    }
    call_b "$@"
    

    在任何 POSIX shell(sh 或 ksh 仿真下的 ash、bash、ksh、zsh 等)中,您可以使用 "$1" "$2" … 构建列表并使用 eval 设置不同的位置参数。

    call_b () {
      i=1
      while [ $i -le $OPTIND ]; do
        a="$a \"\$$i\""
        i=$(($i+1))
      done
      eval set -- $a
      ./b "$@"
    }
    call_b "$@"
    

    通常,这在 zsh 中要容易得多。

    ./b "${(@)@[1,$OPTIND]}"
    

    【讨论】:

      【解决方案2】:

      您为什么使用${@:$OPTIND} 而不仅仅是$@$*

      ${parameter:index} 语法表示使用index 解析$parameter。如果您使用$@,它将使用index 作为参数的索引。

      $ set one two three four #Sets "$@"
      $ echo $@
      one two three four
      $ echo ${@:0}
      one two three four
      $ echo ${@:1}
      one two three four
      $ echo ${@:2}
      two three four
      

      $OPTIND 仅在您使用getopts 时才使用。这会计算getopts 处理$@ 中的参数的次数。根据bash manpage

      每次调用 shell 或 shell 脚本时,都会将 OPTIND 初始化为 1。

      这可以解释为什么您不断获得1 的价值。


      针对已编辑的问题进行了编辑

      @David - "./b $@ " 仍然打印传递给 a 的参数(参见 Q 编辑)。我只想传递 a 而不是 args 的选项

      所以,如果我执行了:

      $ a -a foo -b bar -c fubar barfu barbar
      

      你想传给b:

      $ b -a foo -b bar -c fubar
      

      但不是

      $ b -arg1 foo -arg2 bar -arg3 fubar barfu barbar
      

      这会很棘手......

      您是否有理由不能将整行传递给b 并忽略它?

      我相信可以使用正则表达式:

      $ echo "-a bar -b foo -c barfoo foobar" | sed 's/\(-[a-z] [^- ][^- ]*\)  *\([^-][^-]*\)$/\1/'
      -a bar -b foo -c barfoo
      

      我不能保证这个正则表达式在所有情况下都有效(即如果没有参数怎么办?)。基本上,我将它锚定到行尾,然后匹配最后一个参数和参数以及行的其余部分。我只用最后一个参数和参数进行替换。

      我已经在几种情况下对其进行了测试,但您最好使用getopts 捕获参数,然后自己将这些参数传递给b,或者让b 在可能的情况下忽略这些额外的参数.

      【讨论】:

      • @David - "./b $@ " 仍然打印传递给 a 的参数(参见 Q 编辑)。我只想传递 a 而不是 args 的选项
      • @David - b 是不可变的,而 a 是它的接口。
      • @simpatico - 我认为您无法将所有内容传递给b 是有原因的。正则表达式有用吗?您必须执行类似 $arg2b=$(echo "$@" | sed 'yadda,yadda,yadda') 之类的操作才能获得所需的内容,但语法可能有效。
      • @David:如果选项部分包含特殊字符(空格和通配符),您的正则表达式方法将不起作用;您可以通过set -fIFS 将问题减少到单个字符,但是您做不好)。试试a -a "foo bar":你会得到b -a "foo" "bar"而不是b -a "foo bar"
      • @Gilles - 我提到我的正则表达式可能并非在所有情况下都有效,并表示“使用 getopts 可能会更好”。由于有人已经写了 getopts 答案,我没有详细说明。 getopts 的问题是你必须知道 b 的所有参数。如果 b 改变,程序 a 也必须改变。
      【解决方案3】:

      为了将命令选项与常规参数分开,您需要知道哪些选项带有参数,哪些是独立的。
      在示例命令中:

      ./a -c -d 5 first-arg
      
      • -c-d 可能是独立选项,5 first-arg 可能是常规参数
      • 5 可能是 -d 选项的参数(这似乎是你的意思)
      • -d 可能是 -c 选项的参数,并且(与第一种情况一样)5 first-arg 是常规参数。

      以下是我的处理方式,假设 -a-b-c-d 是唯一的选项,而 -b-d 是唯一带有选项参数的选项.请注意,有必要解析所有选项以找出它们的结束位置。

      #!/bin/bash
      
      while getopts ab:cd: OPT; do
          case "$OPT" in
              a|b|c|d) : ;; # Don't do anything, we're just here for the parsing
              ?)  echo "Usage: $0 [-ac] [-b something] [-d something] [args...]" >&2
                  exit 1 ;;
          esac
      done
      
      ./b "${@:1:$((OPTIND-1))}"
      

      整个while 循环只是为了计算 OPTIND。 getopts 命令中的ab:cd: 定义了允许哪些选项以及哪些选项带有参数(用冒号表示)。神秘的最终表达式表示“参数数组的元素 1 到 OPTIND-1,作为单独的单词传递”。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-23
        相关资源
        最近更新 更多