Bash - 如何避免命令“eval set --”评估变量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35235707/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 14:12:24  来源:igfitidea点击:

Bash - how to avoid command "eval set --" evaluating variables

bashvariablesunixcmdeval

提问by BDR

I just write a little bash script for managing multiple parallels ssh commands. In order to parse arguments I use this piece of code :

我只是写了一个小 bash 脚本来管理多个并行的 ssh 命令。为了解析参数,我使用了这段代码:

#!/bin/bash

# replace long arguments
for arg in "$@"; do
    case "$arg" in
        --help)           args="${args}-h ";;
        --host|-hS)       args="${args}-s ";;
        --cmd)            args="${args}-c ";;
        *) [[ "${arg:0:1}" == "-" ]] && delim='' || delim="\""
           args="${args}${delim}${arg}${delim} ";;
    esac
done

echo "args before eval : $args"
eval set -- $args
echo "args after eval  : $args"

while getopts "hs:c:" OPTION; do
    echo "optarg : $OPTARG"
    case $OPTION in
    h)  usage; exit 0;;
    s)  servers_array+=("$OPTARG");;
    c)  cmd="$OPTARG";;
    esac
done

So I can use for instance -s, --host or -hS for the same result. Everything works fine except one thing.

所以我可以使用 -s、--host 或 -hS 来获得相同的结果。除了一件事,一切都很好。

If I put a variable in argument it will be evaluated.

如果我在参数中放入一个变量,它将被评估。

Explanations

说明

./test.sh -s SERVER -c 'echo $HOSTNAME'
  1. cmdshould be assigned to echo $HOSTNAMEbut because of the eval setcmd is in fact assigned to echo server1(the value of the variable)

  2. If I comment the line eval set -- $argsI cannot use long options (--cmd) but cmdis assigned to echo $HOSTNAMEas expected

  1. cmd应该分配给echo $HOSTNAME但因为eval setcmd实际上分配给echo server1(变量的值)

  2. 如果我评论该行,eval set -- $args我不能使用长选项(--cmd)但按预期cmd分配echo $HOSTNAME

Is there any solution to avoid eval set / getopts to evaluate variables ? So to to have the same behavior as 2. but with long options available.

是否有任何解决方案可以避免使用 eval set/getopts 来评估变量?所以要具有与 2. 相同的行为,但有很长的选项可用。

Examples

例子

with eval set

带评估集

./test.sh -s SERVER -c 'echo $HOSTNAME'
args before eval : -s "SERVER" -c "echo $HOSTNAME"
args after eval  : -s "SERVER" -c "echo $HOSTNAME"
optarg : SERVER
optarg : echo server1

without eval set (line eval set -- $argscommented)

没有评估集(行eval set -- $args注释)

./test.sh -s SERVER -c 'echo $HOSTNAME'
args before eval : -s "SERVER" -c "echo $HOSTNAME"
args after eval  : -s "SERVER" -c "echo $HOSTNAME"
optarg : SERVER
optarg : echo $HOSTNAME

回答by Charles Duffy

As you note, evalis evil-- and there's no need to use it here.

正如你所指出的,eval是邪恶的——这里没有必要使用它。

#!/bin/bash

# make args an array, not a string
args=( )

# replace long arguments
for arg; do
    case "$arg" in
        --help)           args+=( -h ) ;;
        --host|-hS)       args+=( -s ) ;;
        --cmd)            args+=( -c ) ;;
        *)                args+=( "$arg" ) ;;
    esac
done

printf 'args before update : '; printf '%q ' "$@"; echo
set -- "${args[@]}"
printf 'args after update  : '; printf '%q ' "$@"; echo

while getopts "hs:c:" OPTION; do
    : "$OPTION" "$OPTARG"
    echo "optarg : $OPTARG"
    case $OPTION in
    h)  usage; exit 0;;
    s)  servers_array+=("$OPTARG");;
    c)  cmd="$OPTARG";;
    esac
done

That is to say: When building up a command line, append individual items to an array; you can then expand that array, quoted, without risking either evaluation or undesired behavior via effects of string-splitting, glob expansion, etc.

也就是说:在构建命令行时,将单个项附加到数组中;然后,您可以扩展该数组,引用,而不会因字符串拆分、全局扩展等的影响而冒评估或不良行为的风险。