896

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Leonard
  • 13,269
  • 9
  • 45
  • 72
  • 33
    Be flexible, sometimes allow yourself to listen an advice without requiring its deep explanation :) As for `[[` with it the code is good and clear, but remember that day when you'll port your scriptworks on the system with default shell which is not `bash` or `ksh`, etc. `[` is uglier, cumbersome, but works as `AK-47` in any situation. – rook Aug 15 '13 at 20:20
  • 391
    @rook You can listen to an advice without a deep explanation, sure. But when you request an explanation and don't get it, it's usually a red flag. "Trust, but verify" and all that. – Josip Rodin Apr 05 '16 at 11:07
  • 18
    Also see: [What is the difference between the Bash operators [[ vs [ vs ( vs ((?](https://unix.stackexchange.com/q/306111/201820) on Unix & Linux SE. – codeforester Sep 01 '18 at 17:19
  • 28
    @rook in other words "do what you're told and don't ask questions" – 111 Apr 01 '20 at 22:47
  • 29
    @rook That made no sense. –  May 25 '20 at 05:06
  • 4
    Not the question posed, but you should also prefer $(...) for command substitution (e.g. `"$(id -nu)" = "$someuser"` in your code above. Unlike [[...]], $(...) substitution is part of POSIX, and only the very oldest shells don't understand it; it avoids some surprising nesting/quoting behavior that you run into with backticks. – Mark Reed Jul 14 '20 at 12:19
  • 2
    Like @JosipRodin said I'll have doubts if someone claims to know an improvement, but cannot explain what is actually better. Despite of that with every change in some *working* code, there is a chance to break it. So maybe change it if you'll have to change it for some reason, but leave it when there's none. – U. Windl Apr 07 '21 at 07:53
  • Funny that this user recommends the opposite: https://stackoverflow.com/a/13408590/1202615 – LuisCien Sep 30 '21 at 11:36

10 Answers10

855

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

codeforester
  • 39,467
  • 16
  • 112
  • 140
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 41
    Considering that bash is everywhere these days, I tend to think it's pretty damn portable. The only common exception for me is on busybox platforms - but you'd probably want to make a special effort for it anyways, given the hardware that it runs on. – guns Mar 21 '09 at 16:08
  • 30
    @guns: Indeed. I'd suggest that your second sentence disproves your first; if you consider software development as a whole, "bash is everywhere" and "the exception is busybox platforms" are completely incompatible. busybox is widespread for embedded development. – Lightness Races in Orbit Aug 21 '11 at 00:34
  • 15
    @guns: Even if bash is installed on my box, it might not be executing the script (I might have /bin/sh symlinked to some dash-variant, as is standard on FreeBSD and Ubuntu). There's additional weirdness with Busybox, too: with standard compilation options nowadays, it parses `[[ ]]` but interprets it as meaning the same as `[ ]`. – dubiousjim May 31 '12 at 15:42
  • 83
    @dubiousjim: If you use bash-only constructs (like this one), you should have #!/bin/bash, not #!/bin/sh. – Nick Matteo Mar 04 '13 at 19:22
  • 26
    That is why I make my scripts use `#!/bin/sh` but then switch them to use `#!/bin/bash` as soon as I rely on some BASH specific feature, to denote it is no longer Bourne shell portable. – anthony Feb 17 '16 at 06:20
  • 4
    bash is available in ports, but not installed by default, on FreeBSD. Similar on solaris last I checked, but that was quite a while ago. Probably AIX too. Admittedly, much smaller installed bases than Linux distros, but they are real and out there. – Dan Pritts Mar 22 '17 at 17:02
  • 2
    Bash is quite everywhere, but many situations (like those damn crons) don't default to it. I'ts just important to be aware of it. I wouldn't have tried to use [[ ]] in my cron if I had been :) – Balmipour Apr 05 '17 at 09:35
  • 10
    Note that if you install bash on FreeBSD, it won't be `/bin/bash`. It'll be `/usr/local/bin/bash`. The right way to call a non-standard interpreter like bash is `#!/usr/bin/env bash`. – myfreeweb May 06 '17 at 23:42
  • 3
    "But with [, you have to quote $b" For me, it would really help if you showed how to quote it. If $b evaluated to `a*`, would you just quote it like this: `"$b"`? – Daniel Kaplan Aug 31 '17 at 18:24
  • @DanielKaplan Yes. – schuelermine May 16 '23 at 17:22
  • 1
    `[[` originated with ksh in the early 80s, so it tends to be supported in shells that evolved after that -- bash, fish, and zsh all support it. dash is an odd-man out as it was designed to be more strictly POSIX – Chris Dodd Aug 16 '23 at 22:47
389

Behavior differences

Some differences on Bash 4.3.11:

  • POSIX vs Bash extension:

  • 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?

  • <

  • && 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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • Does this hold true "Everything what works in POSIX works in BASH but not vise versa." ? – Wlad Sep 05 '20 at 13:32
  • 1
    @Wlad Bash greatly extends POSIX, so any Bash extension won't be POSIX. The other way around I'm not 100%, but feels likely (except for when Bash extensions override a POSIX syntax, e,g. in posix `[[` would presumably be a regular command too maybe). Related: https://askubuntu.com/questions/766270/what-is-posix-compatible-mode-in-linux – Ciro Santilli OurBigBook.com Sep 05 '20 at 14:08
  • @PauloPedroso obrigado! There's both beauty and horror in mastering legacy tech like Bash. – Ciro Santilli OurBigBook.com Mar 16 '21 at 20:39
  • Why do you not write about `>` which also is `redirection` and `greater than` – Timo Jun 18 '21 at 07:00
  • The distinctive features of bash should be the reason to use them. If you're writing POSIX shell, fine, use whatever. I write bash scripts, and try to use all the sensible features of the language. Yes, they are not portable, but that is OK, Bash is not so difficult to get hold of. Particularly using [[ ]] is safer and more foolproof in many ways. – PePa Oct 06 '21 at 09:02
  • 12
    It's funny to read all of the weird behaviors of `[` compared to the quite sensible behaviors of `[[` and come away with the recommendation to use `[` based solely on portability (which is rarely a concern these days) and that people have to learn the "intricacies" of the bash version. It's more like you have to learn the absurd intricacies of the legacy version. The bash version does what you expect from most other programming languages. – siride Mar 27 '22 at 18:50
  • While I share sentiments towards `[[` being more "reasonable" in comparison to `[`, let me mention that we deal with them in the context of a regular shell, be it Bash or POSIX shell. So if we use Bash, it's *not* new that e.g. `&&` does not have higher priority than `||`, or that we should quote variable expansion, if we don't want word splitting and etc. `[[`, as opposed to `[`, *changes* the rules that are already/anyway there. From that perspective, having to account that extra `[[` specific context, does not sound like a necessarily good thing to me. – Grigory Entin Jun 01 '22 at 22:26
  • Stéphane Chazelas also mentioned in the comments of this [answer](https://unix.stackexchange.com/a/340485/402422) some really peculiar behaviors when using `=~` withing `[[` (I'm horrified by those) which might be another reason that you want to avoid using `=~` and `[[` as this is the few `[[` features without a native `[` equivalent. – Masquue Jul 06 '22 at 10:13
  • 2
    This is a great answer! I would add to it what I consider the major failing of `[[`. Namely, you get no error message when you do integer comparisons incorrectly. eg `[[ $n -gt 0 ]]` vs `[ "$n" -gt 0 ]`. When `n=foo`, that warrants a big screaming error message which you get with `[`. – William Pursell Aug 26 '22 at 21:05
  • 1
    [Here](https://unix.stackexchange.com/a/408003/232523) is a same answer from stackexchange. – liuliang Nov 27 '22 at 07:05
  • 1
    I wish this would be accepted. It contains much more useful information than mine! – Johannes Schaub - litb May 19 '23 at 08:56
70

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 8
    This is far from a bashism, it was first introduced in the Korn shell. – Henk Langeveld Aug 30 '12 at 23:39
  • 11
    @Thomas, the ABS is actually considered a very poor reference in many circles; while it has a great deal of accurate information, it tends to take very little care to avoid showcasing bad practices in its examples, and spent a great deal of its life unmaintained. – Charles Duffy Oct 04 '14 at 01:04
  • 2
    @CharlesDuffy thanks for your comment, can you name a good alternative. I'm not an expert in shell scripting, I am looking for a guide I can consult for writing a script about once every half year. – Thomas Oct 14 '14 at 07:30
  • 13
    @Thomas, the Wooledge BashFAQ and associated wiki are what I use; they're actively maintained by the denizens of the Freenode #bash channel (who, while sometimes prickly, tend to care deeply about correctness). BashFAQ #31, http://mywiki.wooledge.org/BashFAQ/031, is the page directly relevant to this question. – Charles Duffy Oct 14 '14 at 13:18
23

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

li ki
  • 342
  • 3
  • 11
crizCraig
  • 8,487
  • 6
  • 54
  • 53
  • 3
    Google wrote "_no pathname expansion ... takes place_" yet `[[ -d ~ ]]` returns true (which implies `~` was expanded to `/home/user`). I think Google's should have been more precise in it's writing. – JamesThomasMoon Sep 14 '19 at 06:17
  • 2
    @JamesThomasMoon1979 this is tilde expansion, not a pathname expansio mentioned in google text – maoizm Sep 13 '20 at 21:47
  • No need to have quotes around "filename" within [[ ]]. – PePa Oct 06 '21 at 09:06
23

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
f3lix
  • 29,500
  • 10
  • 66
  • 86
  • 10
    What's with the obsession of *fastest* in shell scripts? I want it *most portable* and couldn't care less about the improvement `[[` might bring. But then, I'm an old school old fart :-) – Jens May 12 '12 at 17:11
  • 1
    I think many shells prove builtin versions of `[` and `test` even though external versions also exist. – dubiousjim May 31 '12 at 15:43
  • 4
    @Jens in general I agree: the whole purpose of scripts is (was?) portability (otherwise, we'd code & compile, not script)... the two exceptions I can think of are: (1) tab completion (where completion scripts can get really long with lots of conditional logic); and (2) super-prompts (`PS1=...crazy stuff...` and/or `$PROMPT_COMMAND`); for these, I don't want any _perceptible delay_ in the execution of the script. – michael Aug 01 '13 at 07:22
  • 2
    Some of the obsession with fastest is simply style. All else being equal, why not incorporate the more efficient code construct into your default style, especially if that construct also offers more readability? As far as portability, a good many tasks that bash is suited for are inherently non-portable. E.g., I need to run `apt-get update` if it's been more than X hours since it was last run. It is a great relief when one can leave portability off the already-too-long list of constraints for code. – Ron Burk May 16 '17 at 16:10
20

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.

Mark Reed
  • 91,912
  • 16
  • 138
  • 175
6

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vicente Bolea
  • 1,409
  • 16
  • 39
  • 3
    Given autotools are not a POSIX shell, why would you ever expect `[` to be defined as a POSIX shell function? – Gordon Feb 04 '18 at 15:09
  • Because the autoconf script looks like a shell script, and it produces a shell script, and most shell commands operate inside it. – vy32 Jun 13 '18 at 15:42
1

There is an important caveat to using [[ ]], consider:

$ # For integer like strings, [ and [[ behave the same
$ 
$ n=5 # set n to a string that represents an integer
$ [[ $n -ge 0 ]] && printf "\t> $n is non-negative\n"
        > 5 is non-negative
$ [ "$n" -ge 0 ] && printf "\t> $n is non-negative\n"
        > 5 is non-negative
$ 
$ # But the behavior is different in several cases:
$ n=something # set n to some non-numeric string
$ [ "$n" -ge 0 ] && printf "\t> $n is non-negative\n"
-bash: [: something: integer expression expected
$ [[ $n -ge 0 ]] && printf "\t> $n is non-negative\n"
        > something is non-negative
$ n=5foo
$ [[ $n -ge 0 ]] && printf "\t> $n is non-negative\n"
-bash: 5foo: value too great for base (error token is "5foo")

To clarify the inconsistency of the behavior of [[, consider:

$ for n in 5foo 5.2 something; do [[ $n -ge 0 ]] && echo ok; done
-bash: 5foo: value too great for base (error token is "5foo")
-bash: 5.2: syntax error: invalid arithmetic operator (error token is ".2")
ok
$ for n in 5foo 5.2 something; do [ "$n" -ge 0 ] && echo ok; done
-bash: [: 5foo: integer expression expected
-bash: [: 5.2: integer expression expected
-bash: [: something: integer expression expected

Interpreting some non-numeric strings as 0 and the inconsistent error messages (or complete lack of an error message!) from the line [[ $n -ge 0 ]] when $n is not a valid integer makes [[ unsafe to use for integer comparisons. I strongly advise against [[ for numerical comparisons for this reason.

William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • `[[ $n -gt 0 ]]` and `[ $n -gt 0 ]` are strictly equivalent and should never return an error message. They both return an exit code (0 if the condition is true, 1 if the condition is false) I'm not sure what is the issue you are trying to raise. – arkan Apr 07 '23 at 04:37
  • @arkan if `$n` expands to the string `foo`, then `[ $n -gt 0 ]` should generate an error message like `invalid integer ‘foo’`. This is an extremely useful error message that `[[` does not provide. – William Pursell Apr 07 '23 at 14:08
  • @arkan But perhaps more importantly, the behavior of `[[ foo -lt 5 ]]` is different than `[ foo -lt 5 ]`. The former returns 0, while the latter returns non-zero. IMO, interpreting a non-integer like string as 0 is a fatal flaw. – William Pursell Apr 07 '23 at 15:34
  • Thank you for clarifying, I submitted an edit that should avoid people like me missing your point. "Fatal flaw" or "Unusable" is a bit over the top IMHO (especially talking about bash a legacy pile of hacks). `[[ ]]` does thing that `[ ]` doesn't, so people might want to use it anyway despite this flaw. In addition, it does return the error code '1'. – arkan Apr 08 '23 at 04:10
  • @arkan Thanks for the edit. I made a few changes, which may help clarify further. In the original example, your comment "it does return the error code '1'" is correct, but suggests that you missed an important detail, so I've changed the example to one in which `[` and `[[` behave differently. – William Pursell Apr 08 '23 at 12:28
0

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
scavenger
  • 414
  • 4
  • 10
  • 1
    very true, and not at all inconsequential. bash portability across older versions must be considred. People say "bash is ubiquitous and portable, except for maybe _(insert esoteric OS here)_" -- but in my experience, solaris is one of those platforms where special attention must be paid to portability: not only to consider older bash versions on a newer OS, issues/bugs w/ arrays, functions, etc; but even utilities (used in the scripts) like tr, sed, awk, tar have oddities & peculiarities on solaris that you have to work-around. – michael Aug 01 '13 at 07:37
  • 2
    you are so right... so much utilities non POSIX on Solaris, just look at the "df" output and arguments... Shame on Sun. Hopefully it's disappearing little by little (except in Canada). – scavenger Aug 03 '13 at 17:17
  • 11
    Solaris 2.6 seriously? It was released in 1997 and ended support in 2006. I guess if you're still using that then you have other problems!. Incidentally it used Bash v2.02 which was the one that introduced double brackets so should work even on something as old as that. Solaris 10 from 2005 used Bash 3.2.51 and Solaris 11 from 2011 uses Bash 4.1.11. – peterh Oct 25 '13 at 07:15
  • 1
    Re Script portability. On Linux systems there's generally only edition of each tool and that is the GNU edition. On Solaris you typically have a choice between a Solaris-native edition or the GNU edition (say Solaris tar vs GNU tar). If you depend on GNU specific extensions then you must state that in your script for it to be portable. On Solaris you do that by prefixing with "g", e.g. ` ggrep` if you want the GNU grep. – peterh Oct 25 '13 at 07:24
-1

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.

  • 13
    Test and [ are names for the same builtin command in bash. Try using `type [` to see this. – A B Jul 06 '11 at 17:56
  • 1
    @alberge, that's true, but `[[`, as distinct from `[`, is syntax interpreted by the bash command-line interpreter. In bash, try typing `type [[`. unix4linux is correct that although classic Bourne-shell `[` tests fork off a new process to determine the truth value, the `[[` syntax (borrowed from ksh by bash, zsh, etc) does not. – Tim Gilbert Nov 15 '12 at 19:47
  • 2
    @Tim, I'm not sure which Bourne shell you're talking about, but `[` is built-in to Bash as well as Dash (the `/bin/sh` in all Debian-derived Linux distributions). – A B Nov 18 '12 at 05:20
  • 2
    Oh, I see what you mean, that's true. I was thinking of something like, say, /bin/sh on older Solaris or HP/UX systems, but of course if you needed to be compatible with those you wouldn't be using [[ either. – Tim Gilbert Nov 28 '12 at 17:20
  • 1
    @alberge Bourne shell is not Bash (a.k.a. _Bourne Again SHell_). – apaderno Jul 17 '17 at 20:42
  • I'm pretty sure that by the time Unix System V was released, the `[` and `test` commands were built into the (Bourne) shell — and that was in 1983. I think it may have been built into the System III shell too. They were not built into the original 7th Edition Bourne shell — that was circa 1978. – Jonathan Leffler Jul 29 '22 at 22:29