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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With