Atomiklan
Atomiklan

Reputation: 5434

sh shell double if statement

Can anyone see what I did wrong here? I keep getting the following error message: [[: not found

read INPUT
if [[ "$INPUT" -ge 1 ]] && [[ "$INPUT" -le 10 ]]; then
  Do something
else
  printf "Please enter a value between 1 and 10"
fi

Upvotes: 0

Views: 174

Answers (3)

David W.
David W.

Reputation: 107040

It's complicated:

First, there are three separate ways of constructing your if statement. Each way has its own unique syntax on how to join two booleans. (Actually, there are four ways since one way allows you to use list operators).

A little background...

The if command is a compound command built into the shell. The if command executes the commands following the if. If that command returns a zero value, the if statement is considered true and the then clause executes. Otherwise, if it exists, the else clause will execute. Remember, the if is just a command. You can do things like this:

if ! mv "$foo" "$bar"
then
    echo "I can't move $foo to $bar"
    exit 2
fi

What we need is a command to do some testing for us. If the test succeeds, that test command returns an exit code of zero. If not, it returns a non-zero exit code. Then, it could be used with the if command!

The test command (Yes, there's really one!).

The [ is an alias for the test command which was created to allow you to test files, strings, and numbers for the if statement. (This is now a built in command in Bash, but its roots are actually part of /bin/test and /bin/[). These are the same:

if test "$foo" -eq "$bar"
then
    ...
fi

and

if [ "$foo" -eq "$bar" ]
then
    ...
fi

The test command (if you read the manpage has a -a And test and a -o Or test. You could have done:

if [ "$INPUT" -ge 1 -a "$INPUT" -le 10 ]
then
    ....
fi

This is a single test statement with three test parameters (-ge, -a, and -le).

Using List Operators

This isn't the only way to do a compound boolean test. The Bash shell has two list operators: && and ||. The list operators go in between two commands. If you use && and the left hand command returns a non-zero exit code, the right hand command is not executed, and the entire list returns the exit value of the left-hand command. If you use ||, and the left hand command succeeds, the right hand command is not executed, and the entire list returns a zero exit value. If the first command returns a non-zero exit value, the right-hand command is executed, and the entire list returns the exit value of the right-hand command.

That's why you can do things like this:

[ $bar -eq 0 ] || echo "Bar doesn't have a zero value"!

Since [ ... ] is just a command that returns a zero or non-zero value, we can use these list operators as part of our test:

if [ "$INPUT" -ge 1 ] && [ "$INPUT" -le 10 ]
then
   ...
fi

Note that this is two separate tests and are separated by a && list operator.

Bash's Special [[ compound command

In Kornshell, Zsh, and Bash, there are special compound commands for testing. These are the double square brackets. They appear to be just like the single square brackets command, but because they're compound commands, parsing is affected.

For example:

foo="This has white space"
bar=""   #No value
if [ ! $foo = $bar ]   # Doesn't work!
then

The shell expands $foo and $bar and the test will become:

if [ This has white space = ]

which just doesn't work. However,

if [[ $foo != $bar ]]

works fine because of special parsing rules. The double brackets allow you to use parentheses for grouping and && and || as boolean operators. Thus:

if [[ $INPUT -ge 1 && $INPUT -le 10 ]]
then
   ...
fi

Note that the && appears inside a single set of double square brackets. (Note there's no need for quotation marks)

Mathematical Boolean Expression

Bash has built in mathematical processing including mathematical boolean expressions. If you put something between double parentheses, Bash will evaluate it mathematically:

if (( $INPUT >= 1 && $INPUT <= 10 ))
then
   ...
fi

In this case, (( $INPUT >= 1 && $INPUT <= 10 )) is evaluated. If $INPUT is between 1 and 10 inclusively, the mathematical expression will evaluate as true (zero exit code), and thus the then clause will be executed.

So, you can:

  • Use the original test (single square brackets) command and use the -a to string together two boolean statements in a single test.
  • Use list operators to string together two separate test commands (single square brackets).
  • Use the newer compound test command (double square brackets) that now include && and || as boolean operators, so you have a single compound test.
  • Forget about test command and just use mathematical evaluation (double parentheses) to evaluate boolean expressions.

Upvotes: 1

Todd A. Jacobs
Todd A. Jacobs

Reputation: 84343

Test Constructs Can Vary by Shell

As has been mentioned in other posts, [[ is a Bash shell keyword that isn't present in the Bourne shell. You can see this from a Bash prompt with:

type '[['
[[ is a shell keyword

In a Bourne shell, you will instead get "command not found."

Be More Portable: Use the -a Test Operator

A more portable construct is to use the -a test operator to join conditions (see man test for details). For example:

if [ "$INPUT" -ge 1 -a "$INPUT" -le 10 ]; then
    : # do something when both conditions are true
else
    : # do something when either condition is false
fi

This will work in every Bourne-compatible shell I've ever used, and on any system that has a /bin/\[ executable.

Upvotes: -1

Charles Duffy
Charles Duffy

Reputation: 295403

[[ is not available in scripts which start with #!/bin/sh, or which are started with sh yourscript. Start your script with #!/bin/bash if you want to use it.

See also http://mywiki.wooledge.org/BashGuide/Practices#Choose_Your_Shell


If you are going to use bash, by the way, there's a better syntax for numeric comparisons:

if (( input >= 1 && input <= 10 )); then ...

Note that lower-case variable names are preferred for local use -- all-upper-case names are reserved for environment variables and shell builtins.


If you're not going to use bash, use the POSIX test operator:

if [ "$input" -ge 1 ] && [ "$input" -le 10 ]; then ...

Note that when using [ ] correct quoting is essential, whereas with [[ ]] it is often superfluous; also, [ ] is missing some extensions such as pattern-matching and regular-expression operators.

Upvotes: 6

Related Questions