545

A few months ago I tattooed a fork bomb on my arm, and I skipped the whitespaces, because I think it looks nicer without them. But to my dismay, sometimes (not always) when I run it in a shell it doesn't start a fork bomb, but it just gives a syntax error.

bash: syntax error near unexpected token `{:'

Yesterday it happened when I tried to run it in a friend's Bash shell, and then I added the whitespace and it suddenly worked, :(){ :|:& };: instead of :(){:|:&};:

Does the whitespace matter; have I tattooed a syntax error on my arm?!

It seems to always work in zsh, but not in Bash.

A related question does not explain anything about the whitespaces, which really is my question; Why is the whitespace needed for Bash to be able to parse it correctly?

codeforester
  • 28,846
  • 11
  • 78
  • 104
spydon
  • 5,005
  • 5
  • 22
  • 49
  • 6
    I posted the same question [here](https://unix.stackexchange.com/questions/42348/shell-function-definitionwhy-is-there-a-space-after-the-opening-brace) (excluding the tattoo part). – Benoit Jan 22 '14 at 09:18
  • 3
    Also, the colon (:) can't be used as a function name (see: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01) ... FreeBSD's /bin/sh even gives an error on this ... – Martin Tournoij Jan 24 '14 at 09:58
  • 5
    @Carpetsmoker: I'm not sure how that's relevant. This questions is about Bash. – Dennis May 04 '14 at 17:38

5 Answers5

269

There is a list of characters that separate tokens in BASH. These characters are called metacharacters and they are |, &, ;, (, ), <, >, space and tab. On the other hand, curly braces ({ and }) are just ordinary characters that make up words.

Omitting the second space before } will do, since & is a metacharacter. Therefore, your tattoo should have at least one space character.

:(){ :|:&};:
Toothbrush
  • 2,050
  • 23
  • 33
Dmitri Chubarov
  • 13,673
  • 4
  • 30
  • 64
  • 35
    Easy solution: move the first part of the taoo towards the left and transplant skin between the two parts. –  Jan 23 '14 at 04:49
  • 23
    I liked the term, `on the other hand`... pun intended? ;) :D (Sorry, for off the topic comment.) – anishsane Jun 09 '14 at 14:58
  • 4
    But this is different for zsh? In what way is zsh different? – tfogo Jun 09 '14 at 15:24
  • 2
    Good explanation, except that `{` and `}` are _shell keywords_ in this context. Only if they're not recognized as such - due to lack of surrounding spaces/metacharacters - are they treated as a literal part of the word they become a part of. (And then there's brace expansion, which happens in a different context.) – mklement0 Feb 18 '16 at 21:05
  • 1
    @mklement0 Thank you, good point, `{` and `}` are single character keywords. There is little use in combining them with other characters to form longer words since braces in names are not allowed. Yet you need to separate them with a delimiter. – Dmitri Chubarov Feb 23 '16 at 16:28
  • 2
    @DmitriChubarov Braces are allowed in command names (including functions: `{foo} () { echo hello; }`. "Name", as defined in by `bash` as "a word consisting only of alphanumeric characters and underscores, and beginning with an alphabetic character or an underscore", applies only to variable names. – chepner Sep 20 '16 at 14:55
82

Just tattoo a

#!/bin/zsh

shebang above it and you'll be fine.

Dónal
  • 176,670
  • 166
  • 541
  • 787
SzG
  • 11,409
  • 4
  • 23
  • 37
  • 6
    If we're picky, the shebang would not work at all, as the shell, hopefully zsh, is in interactive mode, as evidenced by the prompt... – SzG Jun 10 '14 at 05:46
50

Braces are more like odd keywords than special symbols, and do need spaces. This is different to parentheses, for example. Compare:

(ls)

which works, and:

{ls}

which looks for a command named {ls}. To work, it has to be:

{ ls; }

The semicolon stops the closing brace being taken as a parameter to ls.

All you have to do is tell people that you are using a proportional-font with a rather narrow space character.

Charles Duffy
  • 235,655
  • 34
  • 305
  • 356
Peter Westlake
  • 4,380
  • 1
  • 22
  • 33
  • @Alfe no, this is not a compound statement. – Dmitri Chubarov Jan 17 '14 at 13:23
  • 12
    @DmitriChubarov - it's a very sneaky trick, using a completely different meaning of braces. It expands the comma-separated list of values, which in this case is just the `ls`. – Peter Westlake Jan 17 '14 at 13:25
  • I was just being silly. Don't take my last comment too earnest ;-) – Alfe Jan 17 '14 at 13:27
  • 7
    But … boy … you are going to have the tattoo for the rest of your life! You marked yourself as a nerd for all time! And _then_ you couldn't even get it right before having it stitched? That's two steps beyond being nerdy I guess ;-) No offense, pal, I'm just wondering. – Alfe Jan 17 '14 at 13:30
  • 14
    @Alfe I tried it before I tattooed it, but I tried it in my zsh, I thought it had the same parsing as bash. Foolish of me, but I'll just tell people that it's zsh. :) – spydon Jan 17 '14 at 13:59
  • 20
    @spydon Or you tell them that you left out the space on purpose so that people copying the command from your tattoo won’t accidentally run it and crash their machine ;) – poke Jan 17 '14 at 17:18
  • @Alfe He is in Australia: reality is upside down there ;-) But, more seriously, can the original then be fixed with a comma before the "}", instead of needing a space after "{"? Maybe the tattoo artist could slip it in? (Or does your comma example just work for `ls`?) – Darren Cook Jan 23 '14 at 01:20
  • No, that opening brace is interpreted as a function block opener because of the part before it, and no matter whether there's a comma in the block or not. – Alfe Jan 23 '14 at 09:10
  • 4
    Hey, if it's valid in zsh, why not just add #!/bin/zsh right above it (or before it)? It's good practice to specify the shell first, anyhow. – Adam Miller Jan 24 '14 at 15:53
41

Although not easily visible in the tatoo font, there's actually a Byte-Order Mark (BOM) between the brace and the colon (you may have been sufficiently intoxicated when you got the tatoo that you didn't notice it, but it's really there). This leaves three obvious possibilities:

  1. You've failed to type in the BOM when you transcribed the code. The result is an obvious application of GIGO. The shell simply doesn't recognize a BOM that isn't present in your failed transcription.
  2. Your shell is too old. It doesn't recognize Unicode characters, so the BOM (and probably all other Unicode characters) is being ignored completely, even though a BOM anywhere but the beginning of a file is supposed to be treated as a zero-width, non-breaking space.
  3. Your shell is too new. Use of a BOM as a ZWNBS is deprecated, and the authors have implemented a future version of Unicode in which this usage is no longer allowed.
Jerry Coffin
  • 437,173
  • 71
  • 570
  • 1,035
40

and then I added the whitespace and it suddenly worked ...

It's because of how the shell parses. You need a space after the function definition begins, i.e. after the {.

foo() { echo hey& }
foo() { echo hey&}
foo(){ echo hey&}

are valid. On the other hand,

foo() {echo hey&}

isn't.


You actually need a tatoo like this:

enter image description here


From the source:

  /* We ignore an open brace surrounded by whitespace, and also
     an open brace followed immediately by a close brace preceded
     by whitespace.  */

Omitting a space after the { causes the {echo to be interpreted as a single token.


An equivalent form of

:(){ :|:& };:

would be

:(){
:|:& };:

Note that there is no space after { in the alternate version, but a line-break causes the shell to recognize { as a token.

Benjamin W.
  • 33,075
  • 16
  • 78
  • 86
devnull
  • 103,635
  • 29
  • 207
  • 208
  • "Omitting a space after the { causes the {echo to be interpreted as a single token." -- so does the parser believe it has encountered a command called `{echo` before it has reached a required opening brace? – Jonah Nov 22 '19 at 02:24