7

Could someone please explain why this works, specifically the fact that I am not using ‘$’ character before the names of the variables inside the if statement? I have searched the Bash Reference Manual, but could not find an explanation.

#!/usr/bin/env bash

one="$1"
two="$2"
three="$3"
four="$4"

if [[ one -le two ]] && [[ three -ge four ]]; then
        echo "TRUE:  $one <= $two && $three >= $four"
else
        echo "FALSE: $one <= $two && $three >= $four"
fi

I have also tested it with a loop like this, which works perfectly

for x1 in {1..3}; do
for x2 in {1..3}; do
for x3 in {1..3}; do
for x4 in {1..3}; do ./test $x1 $x2 $x3 $x4;
done; done; done; done | sort
8
  • $x1 $x2 $x3 $x4 has dollar signs, does it not? Commented Nov 5, 2021 at 20:26
  • 1
    It works because -le and -ge interpret their arguments in a numeric context; when you pass something that looks like a variable name in a numeric context, bash tries looking at whether the variable's contents are a number. Commented Nov 5, 2021 at 20:29
  • 2
    As an aside, there is virtually no reason to use the arithmetic comparison operators inside [[ ... ]]. If you can use [[ ... ]] instead of [ ... ], you can also use (( ... )) instead. if (( one <= two && three >= four )); then. Commented Nov 5, 2021 at 20:48
  • 3
    And as a defensive measure, I would prefer to use the dollar sign. If you forget to define one, for example, then (( one -le two )) will silently behave the same as (( 0 -le two )), while (( $one < $two )) will behave like (( < $two )) and produce a syntax error immediately. Commented Nov 5, 2021 at 20:50
  • 3
    ((...)) is specifically designed for arithmetic expressions; it uses "real" comparison operators like <= instead of cryptic legacy operators like -le, and is virtually guaranteed to be available if [[ ... ]] is available. (By which I mean, both [[ ... ]] and (( ...)) are non-standard, but bash provides both, and I'm not aware of any shell that provides [[...]] but not ((...))).) Commented Nov 5, 2021 at 21:31

2 Answers 2

7

Dollar signs are optional inside an arithmetic context. This is any context where a value is going to be interpreted as a number.

  • $(( ... )) creates an arithmetic context in all POSIX shells
  • (( ... )) creates an arithmetic context in bash, including in for ((expr1; expr2; expr3)).
  • let creates an arithmetic context in shells (like bash) that support that ancient, pre-POSIX, nonstandard syntax.
  • In ${array[idx]} or array[idx]=value, idx is evaluated as arithmetic as long as the array has not been declared to be associative.
  • In [[ value1 -le value2 ]], because -le is an arithmetic operator (it only does numeric comparisons, not string comparisons), both value1 and value2 are parsed as arithmetic. This is also true for -eq, -ne, -lt, -gt and -ge.
  • In ${string:start:len}, both start and len are arithmetic contexts.
  • When declare -i variable has declared a variable to have a numeric type, and variable=value is subsequently run, value is an arithmetic context.

Note that this is not true for arithmetic comparisons inside test or [ commands; test (whether or not called under the name [) acts like a regular shell command rather than special syntax (despite having a built-in implementation as a performance optimization).

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

1 Comment

I am surprised by the substring case ${parameter:offset:length} and the declare -i case. The manual does say so: length and offset are arithmetic expressions (see ARITHMETIC EVALUATION below)., but I never registered that.
6

In the description of Bash Conditional Expressions the description of the arithmetic comparison operators (-lt, -gt, etc.) says:

When used with the [[ command, Arg1 and Arg2 are evaluated as arithmetic expressions (see Shell Arithmetic).

And when you follow that link it says:

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

And the description of Arithmetic Expansion -- $((expression)) says:

All tokens in the expression undergo parameter and variable expansion, command substitution, and quote removal. ... The evaluation is performed according to the rules listed below (see Shell Arithmetic).

5 Comments

Perfect answer, exactly what I was looking for, unlike the shoot from the hip approach and the unnecessary rhetoric that we see so much of these days. Thanks very much!
@corisco, if your intended question was "Why does this work?", you might have asked that; what you actually asked was "When does this work?". The "when" question reads as asking for a list of the contexts where the given thing works, whereas the answer you accepted explains why it works, but not what/where the relevant contexts are, and thus doesn't comprehensively explain when one is or isn't in such a context. Point being that "exactly what [you were] looking for" appears to differ from what you actually requested.
@CharlesDuffy The question begins "Could someone please explain why this works" and then goes on to say that he couldn't find it in the documentation. I found it in the documentation for him.
@Barmar, ahh. I tend to read the title as the core question itself (as it's what people other than the OP who find the question in a search engine / the front page / etc will be using to make their decision on whether to click through and read), and the body as mere focus and clarification; but if one ignores the title, that makes a great deal of sense.
@CharlesDuffy I tend to treat the title as a brief summary, and don't expect it to express all the nuances.

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.