bash 数组可以用来代替 eval 集——“$params”吗?

bash 数组可以用来代替 eval 集——“$params”吗?

我正在看一下optparse库对于bash选项解析,特别是生成代码中的这一点:

params=""
while [ $# -ne 0 ]; do
  param="$1"
  shift

  case "$param" in

    --my-long-flag)
      params="$params -m";;
    --another-flag)
      params="$params -a";;
    "-?"|--help)
      usage
      exit 0;;
    *)
      if [[ "$param" == --* ]]; then
        echo -e "Unrecognized long option: $param"
        usage
        exit 1
      fi
      params="$params \"$param\"";;  ##### THIS LINE
  esac
done

eval set -- "$params"  ##### AND THIS LINE

# then a typical while getopts loop

在这里使用有什么真正的理由吗eval?输入到eval 似乎进行适当的消毒。但使用它不是同样有效吗:

params=()
# ...
    --my-long-flag)
      params+=("-m");;
    --another-flag)
      params+=("-a");;
# ...
      params+=("$param");;
# ...
set -- "${params[@]}"

这对我来说似乎更干净。

事实上,这是否允许通过使用而不是直接从数组中解析选项params(甚至不使用set)?while getopts "ma" option "${params[@]}"; dowhile getopts "ma" option; do

答案1

问题是 ”应该使用 bash 数组代替 eval 集 — “$params”?”,答案是是的!

在您的脚本中,eval 的输入显然是不是适当消毒。尝试

    yourscript '`xterm`'

您将看到 xterm 已启动,即使反引号已正确用单引号引起来。 (与之比较

    echo '`xterm`'

它不会启动 xterm。)

在保留的同时修复bugeval是非常困难的。甚至换线

    params="$params \"$param\"";;

    params="$params '$param'";;

无济于事:现在

    yourscript '`xterm`'

不再启动 xterm,但是

    yourscript \'' `xterm` '\'

仍然如此。

答案2

你不需要在这里使用bash数组(但如果感觉更好的话就这样做)。

以下是如何做到这一点/bin/sh

#!/bin/sh

for arg do
    shift

    case "$arg" in
        --my-long-flag)
            set -- "$@" -m ;;
        --another-flag)
            set -- "$@" -a ;;
        "-?"|--help)
            usage
            exit 0 ;;
        --*)
            printf 'Unrecognised long option: %s\n' "$arg" >$2
            usage
            exit 1 ;;
        *)
            set -- "$@" "$arg"
    esac
done

这比bash数组解决方案更干净(个人意见),因为它不需要引入另一个变量。它也比您显示的自动生成的代码更好,因为它将每个命令行参数保留为单独的项目"$@"。这很好,因为这允许用户传递包含空格字符的参数,只要它们被引用即可(自动生成的代码不这样做)。


风格点评:

  • 上面的循环应该将长选项转换为短选项,以便稍后循环getopts。因此,它实际上打破了该任务表演某些选项,例如-?--help。恕我直言,这些应该被翻译成-h(或一些合适的简短选项)。

  • 它还可以翻译长选项超过了不应接受选项的程度。调用脚本为

    ./script.sh --my-long-flag -- -?
    

    应该由于(意思是“选项在此结束”),因此不解释-?为选项。--同样地,

    ./script.sh filename --my-long-flag
    

    应该不解释--my-long-option为选项,因为选项的解析应在第一个非选项处停止。

这是考虑到上述情况的一个变体:

#!/bin/sh

parse=YES

for arg do
    shift

    if [ "$parse" = YES ]; then
       case "$arg" in
            --my-long-flag)
                set -- "$@" -m ;;
            --another-flag)
                set -- "$@" -a ;;
            --help)
                set -- "$@" -h ;;
            --)
                parse=NO
                set -- "$@" -- ;;
            --*)
                printf 'Unrecognised long option: %s\n' "$arg" >$2
                usage
                exit 1 ;;
            *)
                parse=NO
                set -- "$@" "$arg"
        esac
     else
        set -- "$@" "$arg"
     fi

done

这是做什么的不是允许是长选项分离选项参数,例如--option hello(将hello被视为非选项,选项解析将结束)。--option=hello不过,只要进行一些额外的修改,类似的事情就很容易处理。

相关内容