Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

eval as if statement condition

Why is the output of the following statement true?

VAR="[[ "x"=="y" ]]"
if eval $VAR ; then
    echo "true"
else
    echo "false"
fi

Is there any other way to use a variable as condition for an if statement in bash?

like image 779
AlphaBeta Avatar asked Mar 14 '23 22:03

AlphaBeta


2 Answers

Your quoting is an issue and you need spaces around ==:

VAR='[[ "x" == "y" ]]'
if eval $VAR ; then
    echo "true"
else
    echo "false"
fi

However you must avoid using eval as far as possible. You can use a function here:

fvar() { [[ "x" == "y" ]]; }

Then call it as:

if fvar; then
    echo "true"
else
   echo "false"
fi

Reasons why your eval condition is evaluating to true because you have VAR="[[ "x"=="y" ]]" where the text inside [[ and ]] is being treated as a single string. Which is same as evaluating [[ a ]] or [[ foo ]]

like image 88
anubhava Avatar answered Mar 23 '23 06:03

anubhava


My first answer attempt was voted down, so I'll try to clarify in hopes it at least makes it back to 0 and add something of value considering the other answer. I'll admit I was a bit unclear what you were asking.

First, are your x and y supposed to be literals or variables? Part of the reason I'm uncertain is the extra quoting in the assignment. Note these are equivalent:

$ VAR="[[ "x"=="y" ]]"
$ echo $VAR
[[ x==y ]]
$ VAR="[[ x==y ]]"
$ echo $VAR
[[ x==y ]]

So in this case, saying

if eval $VAR ; then

is the same as saying

if [[ x==y ]]

When you're having trouble with eval, you'll want to print out the value of your variable like this to see what's being executed.

The x==y is one argument, and the [[ operator on a single string argument says, "is this a non-zero length string?" For example:

$ if [[ dkfjek ]]; then echo true; else echo false; fi
true
$ if [[ any_non_zero_length_string ]]; then echo true; else echo false; fi
true
$ if [[ "" ]]; then echo true; else echo false; fi
false

To use the == you need whitespace. For example, without and with eval:

$ if [[ x == x ]]; then echo true; else echo false; fi
true
$ if [[ x == y ]]; then echo true; else echo false; fi
false
$ VAR="[[ x == x ]]"; if eval $VAR; then echo true; else echo false; fi
true
$ VAR="[[ x == y ]]"; if eval $VAR; then echo true; else echo false; fi
false

Usually though you really want to avoid eval unless you've really nailed down what the value of the variable can be. Consider a code snippet where you let the user decide the condition to test: you might write something like this in a script:

read -p "type condition> " COND; 
VAR="[[ $COND ]]"; 
if eval $VAR; 
then 
  echo true; 
else 
  echo false;
fi

You might run it expecting input/output like:

type condition> x == x
true

But what if someone entered this?

type condition> x == x ]]; touch /tmp/oh-no; [[ true
true

What was $VAR? What about /tmp/oh-no?

$ echo $VAR
[[ x == x ]]; touch /tmp/oh-no; [[ true ]]
$ ls -l /tmp/oh-no 
-rw-r--r-- 1 splante isiusers 0 Nov  3 15:17 /tmp/oh-no

Remember the syntax is "if <command>". The "[[" is just one possible command, but any compound command can be executed following if. Now suppose instead of touch /tmp/oh-no the use entered some other more destructive command? It would have executed too.

You asked "Is there any other way to use a variable as condition for an if statement in bash?" This is the typical way to compare two variables for string equivalence in bash:

if [[ "$x" = "$y" ]]; then ...

I assume now you already knew this, but I almost never use eval and just try to work the problem out differently so you don't need to have the whole condition be a variable.

like image 38
Scott Plante Avatar answered Mar 23 '23 06:03

Scott Plante