Consider the following code. If I remove the eval
and instead just write if "$1"
it doesn't work. Is it possible to get rid of the 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/* ]]"
It is possible to create a useful assert
function without using 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
}
Example uses:
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
The assert
function works when the command ($1
) is a user-defined function (e.g. dostuff
), a Bash built-in command (e.g. [
or test
), or an external command (e.g. grep
).
Because of the way that Bash processes lines of code, the function does not work if the command is a Bash reserved word (aka "keyword"). See the accepted answer to What's the difference between shell builtin and shell keyword? for excellent relevant information. In particular, it covers important differences between [ ... ]
and [[ ... ]]
. The standard Bash documentation is poor in this area.
[[
is a reserved word, so this does not work:
assert [[ /tmp/a = '/tmp/*' ]] # Error: '[[' command not found
Since Bash does special processing on the code inside [[ ... ]]
, I don't think it is possible to modify the assert
function to make assert [[ ...
work in all cases. One way to work around the problem, without using eval
, is to use [
instead of [[
in cases where their functionality overlaps (file access testing, simple string comparisions, ...) and use special assert_*
functions for special functionality of [[
. For instance, this function tests (glob) pattern matching:
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
}
Example uses:
assert_match /tmp/a '/tmp/*' # match OK
assert_match /tmp/a '/tmp/b*' # match FAILED
An assert_rematch
function for regular expression matches ([[ ... =~ ... ]]
) might also be useful.