带参数和 eval 的条件构造

Conditional construct with parameter and eval

考虑以下代码。如果我删除 eval 而只是写 if "" 它不起作用。是否可以去掉 eval?

assert () {
  if eval ""      #Is it possible to get rid of the eval?
  then
    echo "Assertion OK:  \"\""
  else
    echo "Assertion FAILED:  \"\""
  fi
}

assert " [[ /tmp/a = /tmp/* ]]"

可以在不使用 eval 的情况下创建一个有用的 assert 函数:

function assert
{
    local IFS   # Protect "$*" against an unusual IFS value in the caller

    if "$@"; then
        printf 'Assertion OK: "%s"\n' "$*"
    else
        printf 'Assertion FAILED: "%s"\n' "$*"
    fi
}

示例使用:

assert [ -f /etc/passwd ]   # OK
assert [ ! -f /etc/group ]  # FAILED

var1='a b'
var2='a b'
var3='-c'
assert [ "$var1" = "$var2" ]    # OK
assert [ "$var2" = "$var3" ]    # FAILED

assert grep -q '^guest:' /etc/passwd    # Maybe OK, maybe FAILED

当命令(</code>)是用户自定义函数(例如<code>dostuff),Bash内置命令(例如[test),或外部命令(例如 grep)。

由于 Bash 处理代码行的方式,如果命令是 Bash reserved word (aka "keyword"). See the accepted answer to What's the difference between shell builtin and shell keyword? 以获得极好的相关信息,该函数将不起作用。特别是,它涵盖了 [ ... ][[ ... ]] 之间的重要区别。标准 Bash 文档在这方面很差。

[[是保留字,所以这个不行:

  assert [[ /tmp/a = '/tmp/*' ]]          # Error: '[[' command  not found

由于Bash对[[ ... ]]里面的代码做了特殊处理,我觉得不可能修改assert函数让assert [[ ...在里面工作所有情况。在不使用 eval 的情况下解决该问题的一种方法是在功能重叠的情况下使用 [ 而不是 [[(文件访问测试、简单的字符串比较,...)并使用特殊的 assert_* 函数实现 [[ 的特殊功能。例如,此函数测试 (glob) 模式匹配:

function assert_match
{
    local -r str=
    local -r pat=

    # shellcheck disable=SC2053 # (Bad warning about not quoting $pat)
    if [[ $str == $pat ]]; then
        printf 'assert_match OK: "%s" matches "%s"\n' "$str" "$pat"
    else
        printf 'assert_match FAILED: "%s" does not match "%s"\n' "$str" "$pat"
    fi
}

示例使用:

assert_match /tmp/a '/tmp/*'    # match OK
assert_match /tmp/a '/tmp/b*'   # match FAILED

用于正则表达式匹配 ([[ ... =~ ... ]]) 的 assert_rematch 函数也可能有用。