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

BDR*_*BDR 2 unix variables bash cmd eval

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

#!/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
Run Code Online (Sandbox Code Playgroud)

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

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

说明

./test.sh -s SERVER -c 'echo $HOSTNAME'
Run Code Online (Sandbox Code Playgroud)
  1. cmd应该分配echo $HOSTNAME但由于eval setcmd实际上被分配给echo server1(变量的值)

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

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

例子

与评估集

./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
Run Code Online (Sandbox Code Playgroud)

没有评估集(行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
Run Code Online (Sandbox Code Playgroud)

Cha*_*ffy 9

如你所知,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
Run Code Online (Sandbox Code Playgroud)

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

  • @smeep,`“$@”`是`for`迭代的默认值,并与`for varname do`一起使用(我不应该使用`;`,POSIX不需要它工作-- 允许它是一个 bash 扩展),但没有对要迭代的内容进行任何更具体的描述。 (2认同)