Условная конструкция с параметром и eval - PullRequest
3 голосов
/ 25 февраля 2020

Рассмотрим следующий код. Если я уберу eval и вместо этого просто напишу if "$1", это не сработает. Можно ли избавиться от eval?

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

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

1 Ответ

1 голос
/ 26 февраля 2020

Можно создать полезную функцию assert без использования eval:

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

Функция assert работает, когда команда ($1) - это пользовательская функция (например, dostuff), встроенная команда Bash (например, [ или test) или внешняя команда (например, grep).

Из-за способа, которым Bash обрабатывает строки кода, функция не работает, если команда является Bash зарезервированным словом (он же «ключевое слово»). См. Принятый ответ на В чем разница между встроенной оболочкой и ключевым словом оболочки? для получения отличной релевантной информации. В частности, оно охватывает важные различия между [ ... ] и [[ ... ]]. Стандартная документация Bash в этой области плохая.

[[ является зарезервированным словом, поэтому оно не работает:

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

, поскольку Bash выполняет специальную обработку для код внутри [[ ... ]], я не думаю, что можно изменить функцию assert, чтобы assert [[ ... работал во всех случаях. Один из способов обойти эту проблему без использования eval - это использовать [ вместо [[ в тех случаях, когда их функциональные возможности перекрываются (тестирование доступа к файлам, простые сравнения строк, ...) и использовать специальный assert_* функции для специальных функций [[. Например, эта функция проверяет соответствие (glob) шаблону:

function assert_match
{
    local -r str=$1
    local -r pat=$2

    # 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 для совпадений регулярного выражения ([[ ... =~ ... ]]) также может быть полезно.

...