How to split a shell command over multiple lines when using an IF statement

shsyntax

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?

Best Answer

The line-continuation will fail if you have whitespace (spaces or tab characters1) 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 (which will then be its own separate argument, since the backslash stops the whitespace from being collapsed into the surrounding unquoted whitespace that separates the other arguments); the backslash will have no effect on the subsequent newline.

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