507

How can I split a command over multiple lines in the shell, when the command is part of an if statement?

This works:

if ! fab --fabfile=.deploy/fabfile.py --forward-agent --disable-known-hosts deploy:$target; then rc=1                                                                       
fi

This doesn't work:

# does not work:
if ! fab --fabfile=.deploy/fabfile.py \ 
  --forward-agent \
  --disable-known-hosts deploy:$target; then   
  rc=1
fi

Instead of the whole command executing, I get:

./script.sh: line 73: --forward-agent: command not found

More importantly, what is missing from my understanding of Bash that will help me understand this and similar issues in the future?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Dmitry Minkovsky
  • 36,185
  • 26
  • 116
  • 160
  • 2
    What is the error? I am able to execute `$ if ! cp -n log/server1.log \ > .; then echo no copy; fi` without error, with a newline after `\ ` – Miserable Variable Sep 03 '13 at 19:21
  • 24
    Do you have spaces after the terminal backslashes `\ `? They are pretty hard to see. If you do, you might want to see if you can make your editor either remove trailing spaces or make them more visible. – msw Sep 03 '13 at 19:24
  • 12
    Yes, it was spaces after the terminal backslashes. Totally. Thank you. – Dmitry Minkovsky Sep 03 '13 at 19:25
  • And yes, sorry, I should have posted the "error" (unexpected result)! My bad! Editing now. – Dmitry Minkovsky Sep 03 '13 at 19:27
  • What was your understanding? It's not part of the question neither. – hakre Oct 13 '14 at 10:07
  • As the others put effort to answer the question correctly, consider leaving the problematic part (the spaces after slash) intact in the code snippet. – George Polevoy Nov 18 '17 at 03:27
  • @GeorgePolevoy. Thanks, not sure what happened. I added it. Possibly I stripped the space in the original post, due to not knowing that was the problem. – Dmitry Minkovsky Nov 18 '17 at 14:38

2 Answers2

729

The line-continuation will fail if you have whitespace (spaces or tab characters¹) after the backslash and before the newline. With no such whitespace, your example works fine for me:

$ cat test.sh
if ! fab --fabfile=.deploy/fabfile.py \
   --forward-agent \
   --disable-known-hosts deploy:$target; then
     echo failed
else
     echo succeeded
fi

$ alias fab=true; . ./test.sh
succeeded
$ alias fab=false; . ./test.sh
failed

Some detail promoted from the comments: the line-continuation backslash in the shell is not really a special case; it is simply an instance of the general rule that a backslash "quotes" the immediately-following character, preventing any special treatment it would normally be subject to. In this case, the next character is a newline, and the special treatment being prevented is terminating the command. Normally, a quoted character winds up included literally in the command; a backslashed newline is instead deleted entirely. But otherwise, the mechanism is the same. Most importantly, the backslash only quotes the immediately-following character; if that character is a space or tab, you just get a literal space or tab; the backslash will have no effect on a subsequent newline.

¹ or carriage returns, for that matter, as Czechnology points out. The POSIX shell does not get along with Windows-formatted text files, not even in WSL. Or Cygwin, but at least their Bash port has added an igncr option that you can set -o to make it carriage-return-tolerant.

Mark Reed
  • 91,912
  • 16
  • 138
  • 175
  • 6
    Mark, you know, I must have had whitespaces. I am able to reproduce the error only when adding whitespaces after the `\`s. For example, when adding one after the first `\`, I get `./soundops: line 73: --forward-agent: command not found`. My issues was that I didn't understand this error. Why does having a whitespace result in that error? The whitespace+`\n` "negates" the `\` and delimits a command? – Dmitry Minkovsky Sep 03 '13 at 19:25
  • 96
    A backslash in front of the newline prevents the newline from terminating the command. But just as special escape sequences like "\n" only work with nothing between the backslash and the n, backslash-newline only works with nothing between the backslash and the newline. – Mark Reed Sep 03 '13 at 19:27
  • 24
    Hahaha wow, of course that makes sense. Never saw it that way. Eye-opening, yet so simple: it's just an escaped newline. I hate invisible characters. They'd make so much more sense to me if they were all just visible. Thank you! – Dmitry Minkovsky Sep 03 '13 at 19:29
  • 7
    In most editors you can make visible those invisible characters. – lucasvc Mar 18 '15 at 07:18
  • OK, got it. But indenting the next continuation lines is no problem, eh? – SDsolar Jul 30 '17 at 23:26
  • 1
    The backslash and newline are deleted from the effective command line, but any leading whitespace on the next line is preserved. So whether it's a problem or not depends on whether whitespace would be a problem at that point in the single-line command. – Mark Reed Jul 31 '17 at 03:02
  • 1
    Just echoing some of the comments here - I would have upvoted anyway, but with the clear explanation, I now know that I won't forget this rule! (+1 and thanks a mill)! – Vérace Jun 15 '20 at 18:58
71

For Windows/WSL/Cygwin etc users:

Make sure that your line endings are standard Unix line feeds, i.e. \n (LF) only.

Using Windows line endings \r\n (CRLF) line endings will break the command line break.


This is because having \ at the end of a line with Windows line ending translates to \ \r \n.
As Mark correctly explains above:

The line-continuation will fail if you have whitespace after the backslash and before the newline.

This includes not just space () or tabs (\t) but also the carriage return (\r).

Czechnology
  • 14,832
  • 10
  • 62
  • 88
  • 2
    This fixes the issue created when creating a script in Windows and then using it in Windows bash (e.g. bash -c MyShellScript.sh where MyShellScript.sh was created in Windows editor). You have to save MyShellScript.sh in UNIX format perhaps using notepad++. – BSalita Jun 11 '17 at 13:04
  • ^ For those using notepad++, at the bottom you can see what the line endings of a file are. If it reads Windows (CL CR) then you should change it Unix (LF). You can click on it to change it. – Javier Salas Jun 21 '20 at 17:35
  • 1
    Thanks a lot! Your answer is a huge help , even in 2020! – Alex Nov 12 '20 at 13:27