4

I'd like to have some explanation on how to best use variables in bash conditional expressions [[...]].

I usually write if statement in this way:

var=1;
# some code...
if [[ $var -eq 1 ]]; then echo "ok"; else echo "fail"; fi

and this return ok as I expected.

Now I saw in some script the same similar statement like:

var=1;
# some code...
if [[ var -eq 1 ]]; then echo "ok"; else echo "fail"; fi

The only difference is the missing parameter expansion character $ in the conditional expression [[...]].

I actually expected this statement to give an error, but this syntax is accepted and returns the ok string.

I tested this statement using bash (GNU bash, version 4.3.46), zsh (5.1.1), ksh (93u+ 2012-08-01) and busybox ash (BusyBox v1.23.2).

I only get an error with busybox shell:

ash: var: bad number

I saw in the bash man page, in the ARITHMETIC EVALUATION paragraph, that:

Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax

But I didn't find anything special related to parameter expansion in the CONDITIONAL EXPRESSIONS paragraph.

So, should conditional expression contain $ when referring to variable or not? and why?

3
  • if [[ var -eq 1 ]]; then echo "ok"; else echo "fail"; fi prints fail for me on bash and ksh Commented Nov 30, 2016 at 15:23
  • @anubhava .. what does echo $var return? :) Commented Nov 30, 2016 at 16:16
  • declare -p var shows me -bash: declare: var: not found Commented Nov 30, 2016 at 16:21

2 Answers 2

3

The trigger here is -eq; since it is defined to perform integer comparison, its operands are evaluated in an arithmetic context. This isn't explicitly documented, though.

You should use the $, though. [[ is an extension, so there is no guarantee that it will behave identically in every shell that defines such a construct. In fact, I wouldn't even assume that [[ var -eq 3 ]] will continue to behave this way in future versions of the same shell. (( var == 3 )) is, though, documented to perform expansion of var since you are in a explicit arithmetic context.

Sign up to request clarification or add additional context in comments.

Comments

1

Check the bash man page's sections on Compound Commands. In particular, the following:

   ((expression))
          The  expression  is  evaluated  according to the rules described below
          under ARITHMETIC EVALUATION.  If the value of the expression is non-zero,
          the return status is 0; otherwise the return status is 1.  This is exactly
          equivalent to `let "expression"`.

   [[ expression ]]
          Return a status of 0 or 1 depending on the evaluation of the conditional
          expression expression.  Expressions are composed of the primaries
          described below  under  CONDITIONAL EXPRESSIONS.

If you are evaluating things that require arithmetic, use arithmetic evaluation, and check the CONDITIONAL EXPRESSIONS section for the various things you can do with [[ ... ]]. Conditions in double-square-brackets can evaluate both strings and integers, and sometimes those work the same way .. sometimes not.

From the bash man page, under CONDITIONAL EXPRESSIONS:

   string1 == string2
   string1 = string2
          True  if the strings are equal.  = should be used with the test command for POSIX conformance.  When used with the [[ command, this performs pattern
          matching as described above (Compound Commands).
...
   arg1 OP arg2
          OP is one of -eq, -ne, -lt, -le, -gt, or -ge.  These arithmetic binary operators return true if arg1 is equal to, not equal to, less than, less than
          or equal to, greater than, or greater than or equal to arg2, respectively.  Arg1 and arg2 may be positive or negative integers.

Obviously, the string "1" is the string "1", so if n=1, and you compare $n against the string "1", you'll get a hit. But you should know what you're doing, and that this is not a numeric comparison. And similarly, < and > are NOT numeric comparisons, they are string comparisons. So while "1" < "2", you may be surprised that "11" < "2" as well.

That said, bash is forgiving about what kind of conditions you ask it to evaluate:

bash-4.4$ n=1
bash-4.4$ [[ n -eq 1 ]] && echo yes
yes
bash-4.4$ [[ $n -eq 1 ]] && echo yes
yes
bash-4.4$ (( n == 1 )) && echo yes
yes
bash-4.4$ (( n = 2 )) && echo yes
yes
bash-4.4$ echo "$n"
2

The first one works because n can't be anything but a variable in this context, so bash treats it as such. But you shouldn't rely on this behaviour. Use dollar signs for variables in conditional expressions, and stick with the bash mantra, "always quote your variables".

Inside a double-square-bracket expression in bash, you should use the arithmetic binary operators if you intend your comparison to be of integers.

Note that your busybox build appears to be using ash, which is NOT bash. Ash is the "Almquist shell", an older POSIX shell than bash, written in the late 1980s. Ash is the basis for /bin/sh in FreeBSD, in addition to being preferred often over bash in embedded systems (hence busybox) due to its smaller size.

You might want to consider writing POSIX shell scripts instead of Bash shell scripts. This will mean simplifying some things, as well as jumping through some hoops for others. POSIX does not include double-square-bracket expressions, but it does allow things like [ "$n" -eq 1 ] or [ $(( n + 1 )) -eq 2 ]. And it'll work in busybox. :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.