73

What is the difference between the &&, ||, -a, and -o Unix operators?

What are the restrictions on the usage of both types?

Is it simply that the && and || operators should be used when using flags in the condition?

As in:

[ "$1" = "yes" ] && [ -r $2.txt ]

versus:

[ "$1" = "yes" -a $2 -lt 3 ]
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Victor Brunell
  • 5,668
  • 10
  • 30
  • 46

2 Answers2

170

Rule of thumb: Use -a and -o inside square brackets, && and || outside.

It's important to understand the difference between shell syntax and the syntax of the [ command.

  • && and || are shell operators. They are used to combine the results of two commands. Because they are shell syntax, they have special syntactical significance and cannot be used as arguments to commands.

  • [ is not special syntax. It's actually a command with the name [, also known as test. Since [ is just a regular command, it uses -a and -o for its and and or operators. It can't use && and || because those are shell syntax that commands don't get to see.

But wait! Bash has a fancier test syntax in the form of [[ ]]. If you use double square brackets, you get access to things like regexes and wildcards. You can also use shell operators like &&, ||, <, and > freely inside the brackets because, unlike [, the double bracketed form is special shell syntax. Bash parses [[ itself so you can write things like [[ $foo == 5 && $bar == 6 ]].

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • 1
    So -a and -o are not exactly necessary if you just use the [[ form? Are there any advantages of using [[ $foo == 5 && $bar == 6 ]] vs [ $foo == 5 -a $bar == 6 ]? – Victor Brunell Dec 08 '13 at 04:12
  • 4
    Yes, it seems the `-o` and `-a` flags are deprecated in the standard. They will probably continue to work for a long time, but it's good to be prepared for the future. – janos Dec 08 '13 at 08:14
  • 5
    The suggested POSIX-compatible replacement for `-a` is two separate calls to `test` joined with `&&`: `[ expr1 -a expr2 ]` becomes `[ expr1 ] && [ expr2 ]`. Similarly for `-o` and `||`. – chepner Dec 09 '13 at 15:58
25

-a and -o are the older and/or operators for the test command. && and || are and/or operators for the shell. So (assuming an old shell) in your first case,

[ "$1" = 'yes' ] && [ -r $2.txt ]

The shell is evaluating the and condition.

In your second case,

[ "$1" = 'yes' -a $2 -lt 3 ]

The test command (or builtin test) is evaluating the and condition.

Of course in all modern or semi-modern shells, the test command is built in to the shell, so there really isn't any or much difference.

In modern shells, the if statement can be written:

[[ $1 == yes && -r $2.txt ]]

Which is more similar to modern programming languages and thus is more readable.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Brad Lanam
  • 5,192
  • 2
  • 19
  • 29
  • Does this mean the single bracket [ form is deprecated in favor of the [[ form? – Victor Brunell Dec 08 '13 at 04:14
  • 4
    The [[ form is much easier to use -- it handles many quoting problems. e.g. in the example, `$1` can be left unquoted, even if it may be empty. – Brad Lanam Dec 08 '13 at 14:28
  • 1
    It depends on whether you need to support older shells. All modern OSs have either bash (in /bin (linux), /usr/bin/ or elsewhere) or /usr/bin/ksh which support the [[ form. – Brad Lanam Dec 08 '13 at 14:42
  • @BradLanam Even modern embedded systems typically do not have bash or ksh. – Étienne Jul 13 '15 at 09:43