首先,也是最重要的一点:知道自己在做什么!其次,虽然你应该避免使用 eval,但如果使用它能让代码更简洁,那就继续使用吧。

Section 61.1: 使用 eval

例如,下面的代码将 $@ 的内容设置为给定变量的内容:

a=(1 2 3)
eval set -- "${a[@]}"

这段代码通常与 getopt 或 getopts 配合使用,以将 $@ 设置为上述选项解析器的输出,但也可以用它来创建一个简单的 pop 函数,直接对变量进行无声操作,而无需将结果存储到原始变量中:

isnum()
{
    # is argument an integer?
    local re='^[0-9]+$'
    if [[ -n $1 ]]; then
        [[ $1 =~ $re ]] && return 0
        return 1
    else
        return 2
    fi
}

isvar()
{
    if isnum "$1"; then
        return 1
    fi
    local arr="$(eval eval -- echo -n "\$$1")"
    if [[ -n ${arr[@]} ]]; then
        return 0
    fi
    return 1
}

pop()
{
    if [[ -z $@ ]]; then
        return 1
    fi

    local var=
    local isvar=0
    local arr=()

    if isvar "$1"; then # let's check to see if this is a variable or just a bare array
        var="$1"
        isvar=1
        arr=($(eval eval -- echo -n "\${$1[@]}")) # if it is a var, get its contents
    else
        arr=($@)
    fi

    # we need to reverse the contents of $@ so that we can shift
    # the last element into nothingness
    arr=($(awk <<<"${arr[@]}" '{ for (i=NF; i>1; --i) printf("%s ",$i); print $1; }'

    # set $@ to ${arr[@]} so that we can run shift against it.
    eval set -- "${arr[@]}"

    shift # remove the last element

    # put the array back to its original order
    arr=($(awk <<<"$@" '{ for (i=NF; i>1; --i) printf("%s ",$i); print $1; }'

    # echo the contents for the benefit of users and for bare arrays
    echo "${arr[@]}"

    if ((isvar)); then
        # set the contents of the original var to the new modified array
        eval -- "$var=(${arr[@]})"
    fi
}

Section 61.2: 在 Getopt 中使用 Eval

虽然类似 pop 的函数可能不需要使用 eval,但只要使用 getopt,就必须使用它:

下面的函数接受 -h 作为选项:

f()
{
    local __me__="${FUNCNAME[0]}"
    local argv="$(getopt -o 'h' -n $__me__ -- "$@")"

    eval set -- "$argv"

    while :; do
        case "$1" in
            -h)
                echo "LOLOLOLOL"
                return 0
                ;;
            --)
                shift
                break
                ;;
    done

    echo "$@"
}

没有 eval set -- “$argv” 生成

-h --

而不是想要的 (-h--),随后进入无限循环,因为

-h --

不匹配 -- 或 -h。