test fails with "argument expected" when comparing "(" - ksh

Can someone please explain why this command returns an error (on Solaris ksh):
if [ "(" = "(" ]; then echo 1;fi;
ksh: test: argument expected
The same using bash is OK, and echoes "1" as expected
Also, using [[ ]] is OK.
The problem seems to be the first "("

That command seems to work on my ksh.
However, IIRC in ksh it's recommended to use [[ ]] instead of [ ]. So for a portable solution, I suggest you write it as:
if [[ "(" = "(" ]]; then echo 1; fi;
Do note however that [[]] is subtly different from [] in that wildcard expansions are not done. See http://www.bolthole.com/solaris/ksh-builtins.html#test
Update
For better portability to different shells, I tend to use the built-in test command which in ksh should have the exact same effect as [[]].
if test "(" = "("; then echo 1; fi

Does it work if you change the double quotes to single quotes -- i.e.,
if [ '(' = '(' ]; then echo 1;fi;
EDITED TO ADD: I've just been able to recreate the problem with a Digital UNIX ksh. Single quotes don't work either on this platform, however escaping the brackets does work:
if [ '\(' = '\(' ]; then echo 1;fi;
According to the man page for ksh, it states:
The following characters have a
special meaning to the shell and cause
termination of a word unless
quoted:
; & ( ) | ^ < > <newline> <space> <tab>
...and goes on to define more parsing rules. It seems that the ( is being interpreted as a special character even when quoted (oddly), hence the requirement for escaping. If you type "set -x" at the command line before you try your if, you'll see exactly what the shell is trying to do sent to stderr preceded with a '+' sign, which may help the tracing; e.g.:
$ set -x
$ if [ '(' = '(' ]; then echo 1;fi;
+ [ ( = ( ]
ksh: test: argument expected
$
$ if [ '\(' = '\(' ]; then echo 1;fi;
+ [ \( = \( ]
+ echo 1
1
$
It may be that it depends on the ksh implementation as well -- e.g., ksh-88 vs ksh-93 and later. On Digital UNIX, the implementation is ksh-88.

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 [ ] )

korn shell error [: missing `]'

I am learning Korn shell which is based on Bourne shell. Below is my really simple code.
read ab
if [ $ab = "a" || $ab = "A" ] ; then
echo hi
fi
For some reason || operator is giving me the error:
[: missing `]'
a: command not found
The correct way to write your if condition is:
read ab
if [ "$ab" = "a" ] || [ "$ab" = "A" ]; then
echo hi
fi
With [ ... ], it is essential to put the variables in double quotes. Otherwise, shell will fail with a syntax error if the variables expand to nothing or if their expansion contains spaces.
See also:
Why should there be a space after '[' and before ']' in Bash?
How to use double or single brackets, parentheses, curly braces
BashFAQ - What is the difference between test, single, and double brackets?
If you use ksh or a modern bash you can use the non-standard [[ ... ]] instead of [ ... ].
This has two benefits:
You can use || inside [[ ... ]]
Variable expansions do not need quotes.
This makes it safe and shorter to write
[[ $ab = a || $ab = A ]]

Comparing variables in shell scripts

I have got a project that involves shell scripts and comparing values/variables within them. I have looked here and elsewhere on comparing variables and I have tried all the various examples given but I am running into something that is not as advertised. OS is Solaris10
I have created the following script as a learning experience-
#!/bin/ksh
stest()
{
if $X = $Y
then echo they're the same
else echo they're notthe same
fi
}
X=a
Y=a
stest
echo completed
I keep getting some variation of the following-
using shell sh or ksh-
#./test.sh
./test.sh[2]: a: not found
completed
using shell bash-
#./test.sh
./test.sh: line 5: a: command not found
completed
I have tried enclosing the if $X = $Y line in brackets and double brackets and I get back
[a: not found
or
[[a: not found
If I change the variables X and Y to the numeral "1" I get the same thing-
./test.sh[2]: 1: not found
I have tried enclosing things in single quotes, double quotes & backwards quotes.
Any help is appreciated.
After if, you need a shell command, like anywhere else. $X = $Y is parsed as a shell command, meaning $X is interpreted as a command name (provided that the value of the variable is a single word).
You can use the [ command (also available as test) or the [[ … ]] special syntax to compare two variables. Note that you need spaces on the inside of the brackets: the brackets are a separate token in the shell syntax.
if [ "$X" = "$Y" ]; then …
or
if [[ "$X" = "$Y" ]]; then …
[ … ] works in any shell, [[ … ]] only in ksh, bash and zsh.
Note that you need double quotes around the variables¹. If you leave off the quotes, then the variable is split into multiple words and each word is interpreted as a wildcard pattern. This doesn't happen inside [[ … ]], but the right-hand side of = is interpreted as a wildcard pattern there too. Always put double quotes around variable substitutions (unless you want the value of the variable to be used as a list of filename matching patterns, rather than as a string).
¹ Except on $X the [[ … ]] syntax.
This KornShell (ksh) script should work:
soExample.ksh
#!/bin/ksh
#Initialize Variables
X="a"
Y="a"
#Function to create File with Input
#Params: 1}
stest(){
if [ "${X}" == "${Y}" ]; then
echo "they're the same"
else
echo "they're not the same"
fi
}
#-----------
#---Main----
#-----------
echo "Starting: ${PWD}/${0} with Input Parameters: {1: ${1} {2: ${2} {3: ${3}"
stest #function call#
echo "completed"
echo "Exiting: ${PWD}/${0}"
Output :
user#foo:/tmp $ ksh soExample.ksh
Starting: /tmp/soExample.ksh with Input Parameters: {1: {2: {3:
they're not the same
completed
Exiting: /tmp/soExample.ksh
ksh version:
user#foo:/tmp $ echo $KSH_VERSION
#(#)MIRBSD KSH R48 2013/08/16

How to get first character of variable

I'm trying to get the first character of a variable, but I'm getting a Bad substitution error. Can anyone help me fix it?
code is:
while IFS=$'\n' read line
do
if [ ! ${line:0:1} == "#"] # Error on this line
then
eval echo "$line"
eval createSymlink $line
fi
done < /some/file.txt
Am I doing something wrong or is there a better way of doing this?
-- EDIT --
As requested - here's some sample input which is stored in /some/file.txt
$MOZ_HOME/mobile/android/chrome/content/browser.js
$MOZ_HOME/mobile/android/locales/en-US/chrome/browser.properties
$MOZ_HOME/mobile/android/components/ContentPermissionPrompt.js
To get the first character of a variable you need to say:
v="hello"
$ echo "${v:0:1}"
h
However, your code has a syntax error:
[ ! ${line:0:1} == "#"]
# ^-- missing space
So this can do the trick:
$ a="123456"
$ [ ! "${a:0:1}" == "#" ] && echo "doesnt start with #"
doesnt start with #
$ a="#123456"
$ [ ! "${a:0:1}" == "#" ] && echo "doesnt start with #"
$
Also it can be done like this:
$ a="#123456"
$ [ "$(expr substr $a 1 1)" != "#" ] && echo "does not start with #"
$
$ a="123456"
$ [ "$(expr substr $a 1 1)" != "#" ] && echo "does not start with #"
does not start with #
Update
Based on your update, this works to me:
while IFS=$'\n' read line
do
echo $line
if [ ! "${line:0:1}" == "#" ] # Error on this line
then
eval echo "$line"
eval createSymlink $line
fi
done < file
Adding the missing space (as suggested in fedorqui's answer ;) ) works for me.
An alternative method/syntax
Here's what I would do in Bash if I want to check the first character of a string
if [[ $line != "#"* ]]
On the right hand side of ==, the quoted part is treated literally whereas * is a wildcard for any sequence of character.
For more information, see the last part of Conditional Constructs of Bash reference manual:
When the ‘==’ and ‘!=’ operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below in Pattern Matching
Checking that you're using the right shell
If you are getting errors such as "Bad substitution error" and "[[: not found" (see comment) even though your syntax is fine (and works fine for others), it might indicate that you are using the wrong shell (i.e. not Bash).
So to make sure you are using Bash to run the script, either
make the script executable and use an appropriate shebang e.g. #!/bin/bash
or execute it via bash my_script
Also note that sh is not necessarily bash, sometimes it can be dash (e.g. in Ubuntu) or just plain ol' Bourne shell.
Try this:
while IFS=$'\n' read line
do
if ! [ "${line:0:1}" = "#" ]; then
eval echo "$line"
eval createSymlink $line
fi
done < /some/file.txt
or you can use the following for your if syntax:
if [[ ! ${line:0:1} == "#" ]]; then
TIMTOWTDI ^^
while IFS='' read -r line
do
case "${line}" in
"#"*) echo "${line}"
;;
*) createSymlink ${line}
;;
esac
done < /some/file.txt
Note: I dropped the eval, which could be needed in some (rare!) cases (and are dangerous usually).
Note2: I added a "safer" IFS & read (-r, raw) but you can revert to your own if it is better suited. Note that it still reads line by line.
Note3: I took the habit of using always ${var} instead of $var ... works for me (easy to find out vars in complex text, and easy to see where they begin and end at all times) but not necessary here.
Note4: you can also change the test to : *"#"*) if some of the (comments?) lines can have spaces or tabs before the '#' (and none of the symlink lines does contain a '#')

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