6

When using the Linux shell, I come across the following situation:

$ A=B\
> C
$ echo $A
BC

In my mind, when the newline encountering escape character, it can not be a CR character but it still be a newline. The echo $A should be interpret as echo B newline C and the newline should be an IFS for echo. So the output should be B C instead of BC.

Why do I get the output I do?

2
  • Hi user3872279. I have edited your question slightly primarily for formatting (to emphasize what your question is), and changed the title to be more descriptive. If you feel I changed your intent in any way, feel free to roll back the edit, or edit further yourself. – user Aug 10 '14 at 11:24
  • @MichaelKjörling In fact, because of poor English, I can not describe what I wanna ask in an elegant way. I appreciate that you've got what's in my mind and thank you very much! – user3872279 Aug 10 '14 at 11:48
7

Quoting man bash, section QUOTING:

A non-quoted backslash (\) is the escape character. It preserves the literal value of the next character that follows, with the exception of <newline>. If a \<newline> pair appears, and the backslash is not itself quoted, the \<newline> is treated as a line continuation (that is, it is removed from the input stream and effectively ignored).

This allows you to break very long commands / command sequences (piping and transforming output etc.) in scripts into multiple lines for readability.


To get it to treat the newline as you expect, just wrap the value (and any later use of the variable) in quotes.

$ A="B
> C"
$ echo "$A"
B
C

From the same section:

Enclosing characters in single quotes preserves the literal value of each character within the quotes. ...

Enclosing characters in double quotes preserves the literal value of all characters within the quotes, with the exception of $, `, \, and, when history expansion is enabled, !. The characters $ and ` retain their special meaning within double quotes. The backslash retains its special meaning only when followed by one of the following characters: $, `, ", \, or .

0
2

Answering the "why" as "why is this useful":

The Backslash-newline is used for line continuation to split ovely long lines:

A backslash at the end of a line in a shell script makes the shell ignore the newline for the purposes of executing the script. This is normally used to split long lines in a script file into multiple text lines, which will be handeled as a single script line by the shell.

For example, the command

git log --tags --branches HEAD FETCH_HEAD ORIG_HEAD --graph --decorate --pretty=oneline --simplify-by-decoration

can be written as

git log --tags --branches HEAD FETCH_HEAD ORIG_HEAD \
    --graph --decorate --pretty=oneline --simplify-by-decoration
0
A=B\
C

means "A is equal to the string B, followed by a newline that I'm ignoring, followed by a C"

There is no CR in what you've typed, so far as the shell sees it. Linux/Unix End Of Line is a Line Feed (LF), not CR. The CR is emitted as part of the terminal handling. Most terminals need a Line Feed to drop a line, and a Carriage Return to send the cursor back to the left. The CR is inserted by the kernel, when sending a Line Feed to the terminal, when the terminal needs that - IOW, it is not visible to the shell. Note that, for example, a visual editor might separate the use of CR and LF - the fewest characters to the next piece of screen to be rewritten may well involve an LF (to go straight down the page without changing column).

Slightly more confusing, there is also a input translation for keyboards. The Enter key usually sends a Carriage Return (Control-M). But to recognise a command has been entered, the shell needs to see an End Of Line. An additional stty parameter therefore describes to the kernel terminal handling, that an input CR should be translated to an End Of Line. So the shell still doesn't see a CR.

The end result is that the terminal sends:

A=B\<CR>C<CR>

The shell receives:

A=B\<LF>C<LF>

The shell parses that as "oh, backslash newline - I just ignore that" and ends up with:

A=BC<LF>

And on output the kernel modifies the sequence sent to the terminal during command input as:

A=B\<CR><LF>C<CR><LF>

The kernel processing of terminal handling is managed by the shell command stty and depending on the implementation (Linux, Mac OS X, *BSD), underlying details should be under man termios, man tty_ioctl. man console_ioctl, etc.

1
  • Do you have any materials recommended for me to know more about input translation for keyboards and "The CR is inserted by the kernel, when sending a Line Feed to the terminal, when the terminal needs that - IOW, it is not visible to the shell." – user3872279 Aug 11 '14 at 6:32
0

Caveat: Backslash continues a line only if it is the very last character in the line.

This is somewhat off-topic; I add it here as an easily-overlooked show-stopper. If you inadvertently end a line in your shell script with \ (backslash followed by space or tab) rather than \ you may find your script stops running or otherwise behaves unexpectedly even though backslash appears to be the last character.

If you are editing with vi it is easy to spot this problem with the command :set list, which will place a $ at the end of every line, allowing you to spot trailing spaces or tabs. :set nolist turns this feature of vi off.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.