Test your square brackets
57 points
6 days ago
| 17 comments
| fluca1978.github.io
| HN
Normal_gaussian
4 minutes ago
[-]
I knew that `[` was notionally the same as `test`, but had never 'checked'. I just assumed `[` was a built-in that conformed rather than them actually being the same.

So I very much enjoyed following along with the article, first seeing that `ls -il /bin/test` and `ls -il /bin/[` are at different inodes, then md5summing and seeing they are different, getting concerned, then the article revealing that is the case now (I'm trusting rather than verifying about the separate builds).

I was then very satisfied to recall that `[[` is a built-in (as `[[` isn't portable between all shells).

reply
jcynix
10 minutes ago
[-]
The [[…]] built-in test version was introduced in Peter Korn's korn shell, ksh88 IIRC. As where various other modernized notations like array variables, process substitution or

    $(command)
as a much more readable version of the backquotes version.

https://docs.oracle.com/cd/E36784_01/html/E36870/ksh88-1.htm...

While learning the Bourne shell as acstudent, I was rapidly lured by csh and then tcsh, but Tom Christiansen's pamphlet https://everything2.com/title/csh+programming+considered+har...

and (or?) the appearance of Paul Falstad's Z-Shell saved me ;-0

reply
jcims
1 hour ago
[-]
Reminds me of my rather memorable introduction to special characters invoking functions, seeing this dastardly little quip in the email signature of someone in a mailing list (circa '95 or so).

    :(){:|:&};:
My curiosity piqued, I pasted it into the shell on my terminal in a pure example of FAFO. The poor little Sparc 5 I was using ground to a halt over the course of about ten seconds. The reboot was as hard as the lesson. xD
reply
Normal_gaussian
15 minutes ago
[-]
I prefer

    u(){u|u&};u
AKA, fork u.
reply
dcminter
1 hour ago
[-]
I have this printed on a sweatshirt - it saddens me a little that people who get it are so few and far between these days :'(
reply
meindnoch
44 minutes ago
[-]
reply
mitchell_h
1 hour ago
[-]
such nostalgia. There was a time when you could tell a fair bit about someone if that was in their email signature.
reply
ogogmad
24 minutes ago
[-]
> The reboot was as hard as the lesson.

I'm confused. What happened on reboot?

reply
Normal_gaussian
17 minutes ago
[-]
A hard reboot is where the power goes all the way off, a soft reboot is where it doesn't. A fork bomb makes it very hard / impossible to trigger a soft reboot, forcing you to do a hard reboot.

As an extra sting, a hard reboot can be damaging if the software and hardware is not correctly handling power interruption, which was much more likely in the 90's.

reply
projektfu
14 minutes ago
[-]
Here is the part of the test.c source from V7 Unix:

   main(argc, argv)
   char *argv[];
   {
   
    ac = argc; av = argv; ap = 1;
    if(EQ(argv[0],"[")) {
     if(!EQ(argv[--ac],"]"))
      synbad("] missing","");
    }
    argv[ac] = 0;
    if (ac<=1) exit(1);
    exit(exp()?0:1);
   }
So, if the professor was missing the "[" command, they were missing a link. More likely, they had a weird orthodoxy about using "test" instead of "[" because reasons. One good reason to use "test" instead of "[" in a Bourne shell control statement is if you are running a list of commands between "if" and "then", and the test is the last part of the list.

Another good place to use "test" is if you are not in a control statement and you are just setting $?.

Edit: EQ() is defined as:

   #define EQ(a,b) ((tmp=a)==0?0:(strcmp(tmp,b)==0))
   char *tmp;
reply
joshstrange
1 hour ago
[-]
In college I took a database class, it was pretty basic overall as I had been playing with MySQL for a few years at that point. On the final exam I got a 90/100. The test was 10 questions that just had you write SQL to answer the question. I got all the queries 100% correct... except... I didn't put a ";" after each query. On a written test. I'm still a little bitter about that.
reply
HarHarVeryFunny
20 minutes ago
[-]
Whoa!

/bin/[ as a binary looking for it's own "]" closing bracket!

What a nasty syntax hack!

I wonder what the motivation was for doing this rather than just implementing test expression support directly in the shell?

reply
1f60c
9 minutes ago
[-]
I also love argv[--argc]. Evil genius.

And it's fully legit (from the draft C standard, 5.1.2.2.1, § 2 "Program startup"):

> The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination.

https://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf [pdf]

reply
reactordev
3 hours ago
[-]
The ultimately sad part was the professor in a Sun OS machine.

In a corner with no where to go, giving demerits because his bash was older than he realized.

Reminds me of my college professor that claimed you don’t have to close HTML tags (some you absolutely do) and I proved that you do. Not all of them, but most of them. (Netscape Navigator Days)

reply
Sharlin
34 minutes ago
[-]
Why do you think the prof even used bash? I highly doubt his ancient SunOS machine had a GNU toolchain. `test` and `[` are both POSIX, but if there was no `/bin/[` I doubt the shell in question (original Bourne? Some proprietary Sun shell? Who knows) had it built in either.
reply
reactordev
9 minutes ago
[-]
Bourne /bin/sh I’m certain of it. If it was Sun OS, it was sh. Was just stating that he was sitting on old while trying to teach new and handing out -1’s for leveraging the new.

Terrible…

reply
jonhohle
1 hour ago
[-]
It doesn’t have anything to do with bash (though modern bash may use a built in for `[`). He don’t have the `[` program (usually linked to `test`).
reply
reactordev
1 hour ago
[-]
Which is why later versions of bash have a builtin…

Precisely because those older systems didn’t link to it!

So my comment still stands.

reply
mcc1ane
21 minutes ago
[-]
reply
xg15
1 hour ago
[-]
So if you really want to troll someone, you can put them in quotes.

  if "[" "$foo" "==" "bar" "]"; then ...
reply
meisel
42 minutes ago
[-]
The real solution is that as soon as you need square brackets, switch to a better language than bash
reply
layer8
31 minutes ago
[-]
Why would you need square brackets if test is the same?
reply
CGMthrowaway
51 minutes ago
[-]
I've been spending too much time researching home improvement and thought this was going to be about deck-building.
reply
Wowfunhappy
2 hours ago
[-]
> When I was a young, green, university student, I was forced to use test(1) as the only true way to do testing in shell scripting. […] Yeah, I was also forced to not use semicolons as they were evil (according to my professor, any comment unneeded!).

The author’s professor clearly went overboard, but doesn’t this entire anecdote demonstrate the value of teaching it this way? Having green students call the `test` binary provides more insight into how UNIX operates, and gets them using a UNIX mindset. They can discover the syntactic shortcuts later.

reply
ogogmad
1 hour ago
[-]
Hmm. What if we replaced the whole of bash with the contents of /bin?
reply
Wowfunhappy
1 hour ago
[-]
…you always need some sort of shell to call the binaries, don’t you? Or is that my own lack of UNIX knowledge talking?

I do think it makes sense to have beginners use `sh` instead of `bash`.

reply
andrewcl
54 minutes ago
[-]
The Objective-C folks probably are chuckling their square brackets send messages.
reply
esafak
53 minutes ago
[-]
To me the salient part is that he had an exam on shell scripts?!
reply
Normal_gaussian
9 minutes ago
[-]
I did my degree in 2012-2015; and we had a mandatory first year course on *nix, which IIRC included a test on shell scripting.

Its not really theoretical or applied CS, but it is a core skill for meaningfully doing applied CS.

reply
stabbles
2 hours ago
[-]
Nowadays [ is a builtin. The subprocess for a simple branch would be excessive overhead.
reply
MontyCarloHall
2 hours ago
[-]
It is indeed a builtin, but `/bin/[` still exists for compatibility reasons!

   $ which [
   /bin/[
   $ type [
   [ is a shell builtin
The same is true for the `test` command:

   $ which test
   /bin/test
   $ type test
   test is a shell builtin
reply
stabbles
27 minutes ago
[-]
Right, that's also a good reminder that the builtin `command -V` is typically what you want whenever you use not-always-builtin `which` ;)
reply
theandrewbailey
3 hours ago
[-]
Now do [ ... ] and [[ ... ]]

I'm still not sure when to use one or the other. I use double brackets by default until something doesn't work.

reply
xp84
33 minutes ago
[-]
If shipping something that must run on sh, check your life choices and use [ - otherwise [[ is better.

Honestly though I’ve been much happier since I stopped writing anything complex enough to have conditionals in Shell. Using a real scripting language like Ruby, Python, even PHP or Perl if you know them, is better.

In the Ruby case I just use `%x( … )` when I need to run shell commands (there are some things shell does great like passing pipelines of text through 5 programs) and let the logic part be in Ruby.

reply
PhilipRoman
2 hours ago
[-]
[[...]] is non-portable and has an extremely quirky corner case with variable expansion in arithmetic contexts, what's not to love?
reply
account42
1 hour ago
[-]
It also does wildcards though, with POSIX you'll need a case statement for that.
reply
ndsipa_pomu
1 hour ago
[-]
I'm intrigued - any info on that?

I personally use ((...)) for arithmetic tests and [[...]] for all other tests as I just target new versions of BASH and don't care much about POSIX compatibility.

reply
nickjj
2 hours ago
[-]
[[ ... ]] supports regex comparisons and lets you combine multiple conditions in a single bracket group using && and || instead of remembering to use -a / -o.

I usually default to [ ... ] unless I need features that double brackets provide.

reply
stabbles
2 hours ago
[-]
Double brackets are less portable. For example musl linux does not come with bash by default, and your script fails.

When unsure, use shellcheck.

reply
duskdozer
2 hours ago
[-]
You mean shellcheck will detect when single brackets won't be enough? I've also just defaulted to double because I never really looked into it
reply
stabbles
31 minutes ago
[-]
Yeah, if you set the shebang `#!/bin/sh` (which is portable), shellcheck will complain about double brackets. It also helps you do quoting correctly when using single brackets.
reply
a3w
2 hours ago
[-]
[[ is built in, so "test[" as an /usr/bin artifact never exists? (What to call that proposed program, test2, or test[ ?)
reply
zzzeek
46 minutes ago
[-]
yeah, if [ is a command in /bin/ what is [[ ?
reply
SAI_Peregrinus
30 minutes ago
[-]
A builtin part of the shell, just like `if` and `then` and `fi`. Not all shells have `[[`, the one that doesn't which people unknowingly run into most is probably `dash` as used by Alpine Linux. POSIX doesn't require `[[` in a shell, BASH & Zsh support it but others often don't.
reply
zzzeek
15 minutes ago
[-]
right so "[" is a file in /bin, "[[" is implicitly part of the shell, so that's as bad as I thought
reply
ndsipa_pomu
1 hour ago
[-]
Use ((...)) for arithmetic tests and [[...]] for other tests. [...] is for POSIX compatibility and not as useful as [[...]] though I don't remember the specifics.
reply
jonhohle
1 hour ago
[-]
[[…]] is a bash (probably other shells, too) built in. […] could be a built in, or could be a symlink to /bin/test.
reply
jcynix
26 minutes ago
[-]
The [[…]] version was introduced in Peter Korn's korn shell, ksh88 IIRC.
reply
ndsipa_pomu
1 hour ago
[-]
Here's Greg's Wiki about the difference between [, [[ and test

https://mywiki.wooledge.org/BashFAQ/031

reply