Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional construct with parameter and eval

Tags:

bash

eval

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/* ]]"
like image 276
Roland Avatar asked Dec 05 '25 05:12

Roland


1 Answers

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.

like image 94
pjh Avatar answered Dec 07 '25 20:12

pjh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!