Vicky
Vicky

Reputation: 1235

Why does [ a -gt b ] (not [ "$a" -gt "$b" ]) appear to work?

I have one problem in unix shell scripting. Let me ask you with very simple example.

suppose, I am getting user input and comparing two numbers.

echo "Enter the first number"
read a
echo "Enter the second number"
read b
if [ a -gt b ]---------------------->I have not used $ before variable name.
then
    echo "a is greater"
else
    echo "b is greater"
fi

In this example, i should have used $ to get the value of variable. By mistake, I forgot. Still, it gives me right result, how and why?

I tried to debug by using sh -x filename. but, it doesn't show any value while comparing(because i havn't used $ sign).

How the shell decide which is greater and vice versa? How it works internally?

Thanks.

Upvotes: 2

Views: 256

Answers (2)

user4815162342
user4815162342

Reputation: 155196

There is a simple explanation why your test appears to work: it blindly executes the else clause.

The [ ... ] built-in has no way to report the error to the caller except by exiting with a non-zero exit status. Incidentally, a non-zero exit status is also used to indicate "false". This means that if cannot distinguish between a false and an erroneous evaluation of its condition, which is why your comparison always prefers the else branch and thus appears to work. You can easily test it by reverting the arguments:

$ if [ b -gt a ]
then
    echo "b is greater"
else
    echo "a is greater"
fi
dash: 11: [: Illegal number: b
a is greater

Note that the error message printed to standard error is, as far as the if statement is concerned, a mere side effect of the test command, and is entirely ignored.

Upvotes: 2

Charles Duffy
Charles Duffy

Reputation: 295619

Notably, you could use this without a $ if you did the comparison in a numeric context.

In bash:

if (( a > b )); then
  echo "a is greater"
else
  echo "b is greater"
fi

...would be correct, as (( )) (double parens) enters a numeric context within which all textual strings are treated as variables and automatically dereferenced (and within which operators such as > and < have their typical arithmetic meanings, rather than performing redirections).

What you're doing now is heavily implementation-dependent. [ a '>' b ] would be a lexographic comparison between the letters a and b. [ a -gt b ] is an arithmetic test.

Many shells will not allow this at all. For instance:

$ a=2; b=3; [ a -gt b ]; echo $?
-bash: [: a: integer expression expected
2

Notably, this is different inside of bash's [[ ]] test context (which doesn't carry the POSIX semantics of [ ]). There, what you're doing actually does work, though it's harder to read than the (( )) form:

$ a=2; b=3; [[ a -gt b ]]; echo $?
1
$ a=3; b=2; [[ a -gt b ]]; echo $?
0

If you're limited to POSIX sh, you don't have (( )), but you do have $(( )), which you can use almost the same way:

$ a=2; b=3; result=$(( a > b ? 1 : 0 )); echo "$result"
0
$ a=3; b=2; result=$(( a > b ? 1 : 0 )); echo "$result"
1

Upvotes: 6

Related Questions