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