Simple logical operators in Bash - bash

I have a couple of variables and I want to check the following condition (written out in words, then my failed attempt at bash scripting):
if varA EQUALS 1 AND ( varB EQUALS "t1" OR varB EQUALS "t2" ) then
do something
done.
And in my failed attempt, I came up with:
if (($varA == 1)) && ( (($varB == "t1")) || (($varC == "t2")) );
then
scale=0.05
fi

What you've written actually almost works (it would work if all the variables were numbers), but it's not an idiomatic way at all.
(…) parentheses indicate a subshell. What's inside them isn't an expression like in many other languages. It's a list of commands (just like outside parentheses). These commands are executed in a separate subprocess, so any redirection, assignment, etc. performed inside the parentheses has no effect outside the parentheses.
With a leading dollar sign, $(…) is a command substitution: there is a command inside the parentheses, and the output from the command is used as part of the command line (after extra expansions unless the substitution is between double quotes, but that's another story).
{ … } braces are like parentheses in that they group commands, but they only influence parsing, not grouping. The program x=2; { x=4; }; echo $x prints 4, whereas x=2; (x=4); echo $x prints 2. (Also braces require spaces around them and a semicolon before closing, whereas parentheses don't. That's just a syntax quirk.)
With a leading dollar sign, ${VAR} is a parameter expansion, expanding to the value of a variable, with possible extra transformations.
((…)) double parentheses surround an arithmetic instruction, that is, a computation on integers, with a syntax resembling other programming languages. This syntax is mostly used for assignments and in conditionals.
The same syntax is used in arithmetic expressions $((…)), which expand to the integer value of the expression.
[[ … ]] double brackets surround conditional expressions. Conditional expressions are mostly built on operators such as -n $variable to test if a variable is empty and -e $file to test if a file exists. There are also string equality operators: "$string1" == "$string2" (beware that the right-hand side is a pattern, e.g. [[ $foo == a* ]] tests if $foo starts with a while [[ $foo == "a*" ]] tests if $foo is exactly a*), and the familiar !, && and || operators for negation, conjunction and disjunction as well as parentheses for grouping. Note that you need a space around each operator (e.g. [[ "$x" == "$y" ]], not [[ "$x"=="$y" ]]), and a space or a character like ; both inside and outside the brackets (e.g. [[ -n $foo ]], not [[-n $foo]]).
[ … ] single brackets are an alternate form of conditional expressions with more quirks (but older and more portable). Don't write any for now; start worrying about them when you find scripts that contain them.
This is the idiomatic way to write your test in bash:
if [[ $varA == 1 && ($varB == "t1" || $varC == "t2") ]]; then
If you need portability to other shells, this would be the way (note the additional quoting and the separate sets of brackets around each individual test, and the use of the traditional = operator rather than the ksh/bash/zsh == variant):
if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then

very close
if [[ $varA -eq 1 ]] && [[ $varB == 't1' || $varC == 't2' ]];
then
scale=0.05
fi
should work.
breaking it down
[[ $varA -eq 1 ]]
is an integer comparison
where as
$varB == 't1'
is a string comparison.
otherwise, I am just grouping the comparisons correctly.
Double square brackets delimit a Conditional Expression. And, I find the following to be a good reading on the subject: "(IBM) Demystify test, [, [[, ((, and if-then-else"

A very portable version (even to legacy bourne shell):
if [ "$varA" = 1 -a \( "$varB" = "t1" -o "$varB" = "t2" \) ]
then do-something
fi
This has the additional quality of running only one subprocess at most (which is the process [), whatever the shell flavor.
Replace = with -eq if variables contain numeric values, e.g.
3 -eq 03 is true, but
3 = 03 is false. (string comparison)

Here is the code for the short version of if-then-else statement:
( [ $a -eq 1 ] || [ $b -eq 2 ] ) && echo "ok" || echo "nok"
Pay attention to the following:
|| and && operands inside if condition (i.e. between round parentheses) are logical operands (or/and)
|| and && operands outside if condition mean then/else
Practically the statement says:
if (a=1 or b=2) then "ok" else "nok"

if ([ $NUM1 == 1 ] || [ $NUM2 == 1 ]) && [ -z "$STR" ]
then
echo STR is empty but should have a value.
fi

Related

Glob pattern condition in if statement [duplicate]

I'm reading bash examples about if but some examples are written with single square brackets:
if [ -f $param ]
then
#...
fi
others with double square brackets:
if [[ $? -ne 0 ]]
then
start looking for errors in yourlog
fi
What is the difference?
Single [] are posix shell compliant condition tests.
Double [[]] are an extension to the standard [] and are supported by bash and other shells (e.g. zsh, ksh). They support extra operations (as well as the standard posix operations). For example: || instead of -o and regex matching with =~. A fuller list of differences can be found in the bash manual section on conditional constructs.
Use [] whenever you want your script to be portable across shells. Use [[]] if you want conditional expressions not supported by [] and don't need to be portable.
Behavior differences
Tested in Bash 4.3.11:
POSIX vs Bash extension:
[ is POSIX
[[ is a Bash extension inspired from Korn shell
regular command vs magic
[ is just a regular command with a weird name.
] is just the last argument of [.
Ubuntu 16.04 actually has an executable for it at /usr/bin/[ provided by coreutils, but the bash built-in version takes precedence.
Nothing is altered in the way that Bash parses the command.
In particular, < is redirection, && and || concatenate multiple commands, ( ) generates subshells unless escaped by \, and word expansion happens as usual.
[[ X ]] is a single construct that makes X be parsed magically. <, &&, || and () are treated specially, and word splitting rules are different.
There are also further differences like = and =~.
In Bashese: [ is a built-in command, and [[ is a keyword: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword
<
[[ a < b ]]: lexicographical comparison
[ a \< b ]: Same as above. \ required or else does redirection like for any other command. Bash extension.
expr x"$x" \< x"$y" > /dev/null or [ "$(expr x"$x" \< x"$y")" = 1 ]: POSIX equivalents, see: How to test strings for lexicographic less than or equal in Bash?
&& and ||
[[ a = a && b = b ]]: true, logical and
[ a = a && b = b ]: syntax error, && parsed as an AND command separator cmd1 && cmd2
[ a = a ] && [ b = b ]: POSIX reliable equivalent
[ a = a -a b = b ]: almost equivalent, but deprecated by POSIX because it is insane and fails for some values of a or b like ! or ( which would be interpreted as logical operations
(
[[ (a = a || a = b) && a = b ]]: false. Without ( ), would be true because [[ && ]] has greater precedence than [[ || ]]
[ ( a = a ) ]: syntax error, () is interpreted as a subshell
[ \( a = a -o a = b \) -a a = b ]: equivalent, but (), -a, and -o are deprecated by POSIX. Without \( \) would be true because -a has greater precedence than -o
{ [ a = a ] || [ a = b ]; } && [ a = b ] non-deprecated POSIX equivalent. In this particular case however, we could have written just: [ a = a ] || [ a = b ] && [ a = b ] because the || and && shell operators have equal precedence unlike [[ || ]] and [[ && ]] and -o, -a and [
word splitting and filename generation upon expansions (split+glob)
x='a b'; [[ $x = 'a b' ]]: true, quotes not needed
x='a b'; [ $x = 'a b' ]: syntax error, expands to [ a b = 'a b' ]
x='*'; [ $x = 'a b' ]: syntax error if there's more than one file in the current directory.
x='a b'; [ "$x" = 'a b' ]: POSIX equivalent
=
[[ ab = a? ]]: true, because it does pattern matching (* ? [ are magic). Does not glob expand to files in current directory.
[ ab = a? ]: a? glob expands. So may be true or false depending on the files in the current directory.
[ ab = a\? ]: false, not glob expansion
= and == are the same in both [ and [[, but == is a Bash extension.
case ab in (a?) echo match; esac: POSIX equivalent
[[ ab =~ 'ab?' ]]: false, loses magic with '' in Bash 3.2 and above and provided compatibility to bash 3.1 is not enabled (like with BASH_COMPAT=3.1)
[[ ab? =~ 'ab?' ]]: true
=~
[[ ab =~ ab? ]]: true, POSIX extended regular expression match, ? does not glob expand
[ a =~ a ]: syntax error. No bash equivalent.
printf 'ab\n' | grep -Eq 'ab?': POSIX equivalent (single line data only)
awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': POSIX equivalent.
Recommendation: always use []
There are POSIX equivalents for every [[ ]] construct I've seen.
If you use [[ ]] you:
lose portability
force the reader to learn the intricacies of another bash extension. [ is just a regular command with a weird name, no special semantics are involved.
Thanks to Stéphane Chazelas for important corrections and additions.
Inside single brackets for condition test (i.e. [ ... ]), some operators such as single = is supported by all shells, whereas use of operator == is not supported by some of the older shells.
Inside double brackets for condition test (i.e. [[ ... ]]), there is no difference between using = or == in old or new shells.
Edit: I should also note that: In bash, always use double brackets [[ ... ]] if possible, because it is safer than single brackets. I'll illustrate why with the following example:
if [ $var == "hello" ]; then
if $var happens to be null / empty, then this is what the script sees:
if [ == "hello" ]; then
which will break your script. The solution is to either use double brackets, or always remember to put quotes around your variables ("$var"). Double brackets is better defensive coding practice.
[[ is a bash keyword similar to (but more powerful than) the [ command.
See
http://mywiki.wooledge.org/BashFAQ/031 and http://mywiki.wooledge.org/BashGuide/TestsAndConditionals
Unless you're writing for POSIX sh, we recommend [[.
[ is a builtin like printf. Bash syntax expect to see it at the same place as commands. And ] is nothing to Bash except the fact that it is expected by the [ builtin. (man bash / SHELL BUILTIN COMMANDS)
[[ is a keyword like if. Bash syntax starts also expect it at the same place as command but instead of executing it, it enters the conditional context. And ]] is also a keyword ending this context. (man bash / SHELL GRAMMAR / Compound Commands)
In order, bash tries to parse: Syntax Keywords > User Alias > Builtin Function > User Function > Command in $PATH
type [ # [ is a shell builtin
type [[ # [[ is a shell keyword
type ] # bash: type: ]: not found
type ]] # ]] is a shell keyword
compgen -k # Keywords: if then else ...
compgen -b # Builtins: . : [ alias bg bind ...
which [ # /usr/bin/[
[ is slower <= it executes more parsing code I guess. But I know that it calls the same number of syscall (tested with
[[ is syntactically easier to parse even for human as it starts a context. For arithmetical condition, think about using ((.
time for i in {1..1000000}; do [ 'a' = 'b' ] ; done # 1.990s
time for i in {1..1000000}; do [[ 'a' == 'b' ]] ; done # 1.371s
time for i in {1..1000000}; do if [ 'a' = 'a' ]; then if [ 'a' = 'b' ];then :; fi; fi ; done # 3.512s
time for i in {1..1000000}; do if [[ 'a' == 'a' ]]; then if [[ 'a' == 'b' ]];then :; fi; fi; done # 2.143s
strace -cf bash -c "for i in {1..100000}; do if [ 'a' = 'a' ]; then if [ 'a' = 'b' ];then :; fi; fi ; done;" # 399
strace -cf bash -c "for i in {1..100000}; do if [[ 'a' == 'a' ]]; then if [[ 'a' == 'b' ]];then :; fi; fi ; done;" # 399
I recommend using [[: If you do not explicitly care about posix compatibility, it means that you are not, so do no care about getting "more" compatible a script that is not.
you can use the double square brackets for light regex matching, e.g. :
if [[ $1 =~ "foo.*bar" ]] ; then
(as long as the version of bash you are using supports this syntax)
Bash manual says:
When used with [[, the ‘<’ and ‘>’ operators sort lexicographically
using the current locale. The test command uses ASCII
ordering.
(The test command is identical to [ ] )

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
[[ 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.
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.
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.

Bash: command not found

Bash shell, the following code snippet results in the error: "too many arguments". I have searched thoroughly and cannot get to the bottom of this.
if [ [ $i % 3 ] == 0 ] && [ [ $i % 5 == 0 ] ]
I am just learning bash, any help is much appreciated, thanks in advance!
Solved: if [[ $((i % 3)) == 0 && $((i % 5)) == 0 ]] thank you #BroSlow
You can simplify this by using an arithmetic expression:
if (( i % 3 == 0 && i % 5 == 0 )); then
(or even more simply, with (( i % 15 == 0 ))...).
Let me explain this a little more: in bash there are four main types of conditions you'll see in an if command:
[ ... ] -- the [ and ] look like a some kind of parentheses, but [ is actually a synonym for the test command, and evaluates its arguments as a conditional expression. It does not understand additional [ ] pairs as parentheses, you have to use ( ) instead. But because it's a command, all of its arguments are subject to the usual shell parsing before being handed to the command, and so you'd have to escape them like \( ... \), which is annoying. And you can't use && because that separates commands, you have to use -a instead.
And there are a pile of other parsing oddities, like if you use < or > you have to escape them or it'll treat them as input/output redirects. Seriously, if you use if [ 3 > 5 ], it'll create a file named "5", and dump the output of the command [ 3 ] into it. And BTW [ 3 ] doesn't output anything, but it does return success (i.e. true) because "3" is not blank...
Basically, you should not use [ ... ] unless you need compatibility with generic shells that don't support any of the better options.
[[ ... ]] -- this is a bash conditional expression, which is basically a cleaned up version of [ ... ] (aka test), with the weird parsing oddities removed and some cool additional features added. You can use parentheses (not brackets) for grouping, and < and > for comparison, all without escapes. If you're using bash (not a generic shell), this is usually the way to go. But it doesn't do math (it's all string-oriented), unless you force arithmetic evaluation of something with $(( )) (as in BroSlow's answer).
And a warning: in [[ ... ]], =, !=, <, and > do string comparisons, not arithmetic. For example, [[ 05 = 5 ]] is false, because they aren't textually equal. And [[ 10 > 5 ]] is also false, because "1" comes before "5" alphabetically (well, in standard sorting order). If you want arithmetic comparisons here, you need to use -eq, -ne, -lt, and -gt instead.
(( ... )) -- this is an arithmetic expression. Sort of similar to [[ ]], except the contents get evaluated as a mathematical expression instead of string-oriented tests. Note that in a math context, variables get evaluated automatically, so you can use i instead of $i. Also, = is an assignment, while == is a comparison (in [[ ... ]], they're both comparisons).
This is confusingly similar to the $(( ... )) thing I mentioned in the last item. The difference is that (( ... )) acts like an entire command on its own, while $(( ... )) evaluates the contents and returns the result for inclusion in some other command. For example, echo $((3+5)) will evaluate to echo 8, which then prints "8".
Some other command. You can use any command you want as the test in an if statement, and if will execute the then or else branch based on whether the command succeeds or fails. You'll see things like if grep somepattern somefile; then... and if somecommand; then echo "it worked"; else echo "it failed"; fi, and such. All of the other three options are really just special cases of this: you can use [ ...], [[ ... ]], or (( ... )) anyplace you'd use any other command in bash, they just happen to be particularly useful as if conditions.
You can't nest brackets inside of brackets in bash. So [[ ]] is fine [ ] is fine but [ [ ] ] is not (it assumes the nested brackets are operators). And if you want some math you can do $((math))
Should be
if [[ $((i % 3)) == 0 && $((i % 5)) == 0 ]]
Also see
Too many arguments error in bash
you cannot have space between [ and next [. Change [ [ to [[. Same with ] ].
See Conditional expressions in bash.

unary operator expected in shell script when comparing null value with string

I have two variables
var=""
var1=abcd
Here is my shell script code
if [ $var == $var1 ]; then
do something
else
do something
fi
If I run this code it will prompt a warning
[: ==: unary operator expected
How can I solve this?
Since the value of $var is the empty string, this:
if [ $var == $var1 ]; then
expands to this:
if [ == abcd ]; then
which is a syntax error.
You need to quote the arguments:
if [ "$var" == "$var1" ]; then
You can also use = rather than ==; that's the original syntax, and it's a bit more portable.
If you're using bash, you can use the [[ syntax, which doesn't require the quotes:
if [[ $var = $var1 ]]; then
Even then, it doesn't hurt to quote the variable reference, and adding quotes:
if [[ "$var" = "$var1" ]]; then
might save a future reader a moment trying to remember whether [[ ... ]] requires them.
Why all people want to use '==' instead of simple '=' ? It is bad habit! It used only in [[ ]] expression. And in (( )) too. But you may use just = too! It work well in any case. If you use numbers, not strings use not parcing to strings and then compare like strings but compare numbers. like that
let -i i=5 # garantee that i is nubmber
test $i -eq 5 && echo "$i is equal 5" || echo "$i not equal 5"
It's match better and quicker. I'm expert in C/C++, Java, JavaScript. But if I use bash i never use '==' instead '='. Why you do so?

Are double square brackets [[ ]] preferable over single square brackets [ ] in Bash?

A coworker claimed recently in a code review that the [[ ]] construct is to be preferred over [ ] in constructs like
if [ "`id -nu`" = "$someuser" ] ; then
echo "I love you madly, $someuser"
fi
He couldn't provide a rationale. Is there one?
[[ has fewer surprises and is generally safer to use. But it is not portable - POSIX doesn't specify what it does and only some shells support it (beside bash, I heard ksh supports it too). For example, you can do
[[ -e $b ]]
to test whether a file exists. But with [, you have to quote $b, because it splits the argument and expands things like "a*" (where [[ takes it literally). That has also to do with how [ can be an external program and receives its argument just normally like every other program (although it can also be a builtin, but then it still has not this special handling).
[[ also has some other nice features, like regular expression matching with =~ along with operators like they are known in C-like languages. Here is a good page about it: What is the difference between test, [ and [[ ? and Bash Tests
Behavior differences
Some differences on Bash 4.3.11:
POSIX vs Bash extension:
[ is POSIX
[[ is a Bash extension inspired from KornShell
regular command vs magic
[ is just a regular command with a weird name.
] is just the last argument of [.
Ubuntu 16.04 actually has an executable for it at /usr/bin/[ provided by coreutils, but the Bash built-in version takes precedence.
Nothing is altered in the way that Bash parses the command.
In particular, < is redirection, && and || concatenate multiple commands, ( ) generates subshells unless escaped by \, and word expansion happens as usual.
[[ X ]] is a single construct that makes X be parsed magically. <, &&, || and () are treated specially, and word splitting rules are different.
There are also further differences like = and =~.
In Bashese: [ is a built-in command, and [[ is a keyword: What's the difference between shell builtin and shell keyword?
<
[[ a < b ]]: lexicographical comparison
[ a \< b ]: Same as above. \ required or else does redirection like for any other command. Bash extension.
expr x"$x" \< x"$y" > /dev/null or [ "$(expr x"$x" \< x"$y")" = 1 ]: POSIX equivalents, see: How to test strings for lexicographic less than or equal in Bash?
&& and ||
[[ a = a && b = b ]]: true, logical and
[ a = a && b = b ]: syntax error, && parsed as an AND command separator cmd1 && cmd2
[ a = a ] && [ b = b ]: POSIX reliable equivalent
[ a = a -a b = b ]: almost equivalent, but deprecated by POSIX because it is insane and fails for some values of a or b like ! or ( which would be interpreted as logical operations
(
[[ (a = a || a = b) && a = b ]]: false. Without ( ) it would be true, because [[ && ]] has greater precedence than [[ || ]]
[ ( a = a ) ]: syntax error, () is interpreted as a subshell
[ \( a = a -o a = b \) -a a = b ]: equivalent, but (), -a, and -o are deprecated by POSIX. Without \( \) it would be true, because -a has greater precedence than -o
{ [ a = a ] || [ a = b ]; } && [ a = b ] non-deprecated POSIX equivalent. In this particular case however, we could have written just: [ a = a ] || [ a = b ] && [ a = b ], because the || and && shell operators have equal precedence, unlike [[ || ]] and [[ && ]] and -o, -a and [
word splitting and filename generation upon expansions (split+glob)
x='a b'; [[ $x = 'a b' ]]: true. Quotes are not needed
x='a b'; [ $x = 'a b' ]: syntax error. It expands to [ a b = 'a b' ]
x='*'; [ $x = 'a b' ]: syntax error if there's more than one file in the current directory.
x='a b'; [ "$x" = 'a b' ]: POSIX equivalent
=
[[ ab = a? ]]: true, because it does pattern matching (* ? [ are magic). Does not glob expand to files in the current directory.
[ ab = a? ]: a? glob expands. So it may be true or false depending on the files in the current directory.
[ ab = a\? ]: false, not glob expansion
= and == are the same in both [ and [[, but == is a Bash extension.
case ab in (a?) echo match; esac: POSIX equivalent
[[ ab =~ 'ab?' ]]: false, loses magic with '' in Bash 3.2 and above and provided compatibility to Bash 3.1 is not enabled (like with BASH_COMPAT=3.1)
[[ ab? =~ 'ab?' ]]: true
=~
[[ ab =~ ab? ]]: true. POSIX extended regular expression match and ? does not glob expand
[ a =~ a ]: syntax error. No Bash equivalent.
printf 'ab\n' | grep -Eq 'ab?': POSIX equivalent (single-line data only)
awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': POSIX equivalent.
Recommendation: always use []
There are POSIX equivalents for every [[ ]] construct I've seen.
If you use [[ ]] you:
lose portability
force the reader to learn the intricacies of another Bash extension. [ is just a regular command with a weird name, and no special semantics are involved.
Thanks to Stéphane Chazelas for important corrections and additions.
[[ ]] has more features - I suggest you take a look at the Advanced Bash Scripting Guide for more information, specifically the extended test command section in Chapter 7. Tests.
Incidentally, as the guide notes, [[ ]] was introduced in ksh88 (the 1988 version of KornShell).
From Which comparator, test, bracket, or double bracket, is fastest?:
The double bracket is a “compound
command” where as test and the single
bracket are shell built-ins (and in
actuality are the same command). Thus,
the single bracket and double bracket
execute different code.
The test and single bracket are the
most portable as they exist as
separate and external commands.
However, if your using any remotely
modern version of BASH, the double
bracket is supported.
If you are into following Google's style guide:
Test, [ … ], and [[ … ]]
[[ … ]] is preferred over [ … ], test and /usr/bin/[.
[[ … ]] reduces errors as no pathname expansion or word splitting takes place between [[ and ]]. In addition, [[ … ]] allows for regular expression matching, while [ … ] does not.
# This ensures the string on the left is made up of characters in
# the alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
echo "Match"
fi
# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; then
echo "Match"
fi
# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; then
echo "Match"
fi
For the gory details, see E14 at http://tiswww.case.edu/php/chet/bash/FAQ
In a question tagged 'bash' that explicitly has "in Bash" in the title, I'm a little surprised by all of the replies saying you should avoid [[...]] because it only works in bash!
It's true that portability is the primary objection: if you want to write a shell script which works in Bourne-compatible shells even if they aren't bash, you should avoid [[...]]. (And if you want to test your shell scripts in a more strictly POSIX shell, I recommend dash; though it is an incomplete POSIX implementation since it lacks the internationalization support required by the standard, it also lacks support for most, but not all, of the many non-POSIX constructs found in bash, ksh, zsh, etc.)
The other objection I see is at least applicable within the assumption of bash: that [[...]] has its own special rules which you have to learn, while [...] acts like just another command. That is again true (and Mr. Santilli brought the receipts showing all the differences), but it's rather subjective whether the differences are good or bad. I personally find it freeing that the double-bracket construct lets me use (...) for grouping, && and || for Boolean logic, < and > for comparison, and unquoted parameter expansions. It's like its own little closed-off world where expressions work more like they do in traditional, non-command-shell programming languages.
A point I haven't seen raised is that this behavior of [[...]] is entirely consistent with that of the arithmetic expansion construct $((...)), which is specified by POSIX, and also allows unquoted parentheses and Boolean and inequality operators (which here perform numeric instead of lexical comparisons). Essentially, any time you see the doubled bracket characters you get the same quote-shielding effect.
(Bash and its modern relatives also use ((...)) – without the leading $ – as either a C-style for loop header or an environment for performing arithmetic operations; neither syntax is part of POSIX.)
So there are some good reasons to prefer [[...]]; there are also reasons to avoid it, which may or may not be applicable in your environment. As to your coworker, "our style guide says so" is a valid justification, as far as it goes, but I'd also seek out backstory from someone who understands why the style guide recommends what it does.
A typical situation where you cannot use [[ is in an autotools configure.ac script. There brackets have a special and different meaning, so you will have to use test instead of [ or [[ -- Note that test and [ are the same program.
I am surprised that I haven't seen this issue raised sooner, but consider:
$ n=5
$ [[ $n -gt 0 ]] && echo $n is positive
5 is positive
$ n=foo
$ [[ $n -gt 0 ]] && echo $n is positive
$ [ "$n" -gt 0 ] && echo $n is positive
bash: [: foo: integer expression expected
The lack of error message from the line [[ $n -gt 0 ]] renders [[ completely unusable. Perhaps I'm being too harsh and the correct response is to simply avoid integer comparisons in [[, but I strongly advise against [[ for this reason.
In a nutshell, [[ is better because it doesn't fork another process. No brackets or a single bracket is slower than a double bracket because it forks another process.
[[ ]] double brackets are unsupported under certain versions of SunOS and totally unsupported inside function declarations by:
GNU Bash, version 2.02.0(1)-release (sparc-sun-solaris2.6)

Resources