How can I split a shell command over multiple lines when using an IF statement?

SyntaxSh

Syntax Problem Overview


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?

Syntax Solutions


Solution 1 - Syntax

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.

Solution 2 - Syntax

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDmitry MinkovskyView Question on Stackoverflow
Solution 1 - SyntaxMark ReedView Answer on Stackoverflow
Solution 2 - SyntaxCzechnologyView Answer on Stackoverflow