Escaped newlines make the code work wrong. Why?

From a Markdown style guide by Google:

Because most commandline snippets are intended to be copied and pasted directly into a terminal, it’s best practice to escape any newlines. Use a single backslash at the end of the line:

bazel run :target -- --flag --foo=longlonglonglonglongvalue 
--bar=anotherlonglonglonglonglonglonglonglonglonglongvalue

Could someone explain why escaping newlines in the following snippet makes it work wrong if I copy-and-paste it into a terminal (I use zsh on a Mac, not sure the same issue happens in pure Bash and/or in other terminals. Does it?):

If you copy-and-paste this snippet into a terminal, it works fine:

for (( counter=10; counter>0; counter-- ))
do
echo -n "$counter "
done
printf "n"

But if you copy-and-paste this one, there will be an error:

for (( counter=10; counter>0; counter-- )) 
do 
echo -n "$counter " 
done 
printf "n"
Asked By: jsx97

||

followed by a newline is a line-continuation, it’s used to break very long lines into more than one generally for legibility.

The shell upon reading them, just removes them:

ec
ho t
est

Is just another way to write:

echo test

And:

for (( counter=10; counter>0; counter-- )) 
do 
echo -n "$counter " 
done 
printf "n"

Is another way to write:

for (( counter=10; counter>0; counter-- )) do echo -n "$counter " done printf "n"

Which would run echo -n "$counter " done printf "n" in a loop, except it’s missing the done to close the loop (the other done is just an argument to echo).

So you may want to insert some <newline> to break very long lines, but you don’t want to replace <newline>s (which are used to delimit commands among other things) with <newline> which is the same as nothing¹.


¹ except inside single quotes (or $'...' for those shells that support that) or here-documents with quoted delimiters (as in << "EOF"...EOF)

Answered By: Stéphane Chazelas

Newline (NL, ^J) marks End-Of Line, and usually ends a command.

Escaped NL characters are treated as a blank, and don’t end commands, or lines.

foo
bar

are two commands.

foo 
bar

is one command,

foo bar
Answered By: waltinator

In shell syntax, there is a semicolon (;) token which separates commands.

As an alternative a newline can be used. In other words, you can do this:

command ; command

or this:

command
command

Certain syntactic constructs in the shell consist of multiple commands, like looping:

while test-command1; test-comand2 ; do command1; command2; command3; done

Syntactically, this is six commands: a while command which marks the start of a sequence of test commands which control the loop. Then a do command which marks the start of the loop body. Finally a done command that marks the end.

When this is written on multiple lines, the semicolons become optional:

while test-command-1
      test-command-2
do    command1
      command2
      command3
done

Now the backslash-newline sequence, often called a line continuation, turns multiple physical lines into a single logical line. A similar line-continuation feature is found in the Awk language, in the Make language and in the preprocessing feature of the C language.

When we break a shell script into multiple physical lines with line continuations, the interpreter sees only a single line. Therefore, the semicolons are required.

Wrong:

while test-command-1 
      test-command-2 
do    command1 
      command2 
      command3 
done

Right:

while test-command-1 ; 
      test-command-2 ; 
do    command1 ; 
      command2 ; 
      command3 ; 
done

It’s very common to see this in in a Makefile. In Make, every line of a build recipe is a separate shell script run in a separate shell instance. Thus every script must fit into a single logical line of the make syntax. Line continuations are used to break complex shell scripts into multiple physical lines, and so the semicolons are required.

Answered By: Kaz
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.