71

In order to understand another answer (by glenn jackman):

find / -type d -print0 | while read -r -d '' dir; do ls -ltr "$dir" | sed '$!d'; done

the first step is to understand the usage of the option -r of the read command.

First, I thought, it would be sufficient to simply execute

man read

to look up the meaning of the -r option, but I realized the man page does not contain any explanation for options at all, so I Googled for it.  I got some read -t, read -p examples, but no read -r.

3 Answers3

77

There is no stand-alone read command: instead, it is a shell built-in, and as such is documented in the man page for bash:

read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]

        ︙
    -r
      Backslash does not act as an escape character.  The backslash is considered to be part of the line.  In particular, a backslash-newline pair may not be used as a line continuation.

So, to summarize, read normally allows long lines to be broken using a trailing backslash character, and normally reconstructs such lines. This slightly surprising behavior can be deactivated using -r.

dhag
  • 15,736
  • 4
  • 55
  • 65
  • 23
    A demo: str="a\bc"; read x <<< "$str"; read -r y <<< "$str"; echo "$x"; echo "$y" – glenn jackman Mar 27 '15 at 12:47
  • @glennjackman That's great, it's even more striking if you type str="a\ and then hit Enter before pasting in the rest of the command starting with b. – ErikE Jun 20 '18 at 16:52
  • 1
    Why do you consider line continuation by backslash+newline to be the least bit surprising? It’s standard shell behavior, and is documented in the “QUOTING” section of the man page: “ \ is treated as a line continuation”. Try typing da\ (Enter) te (Enter). – G-Man Says 'Reinstate Monica' Oct 21 '20 at 00:59
  • 1
    @G-ManSays'ReinstateMonica': That's an excellent question, and, thinking about it, while it is true that read is consistent with Bash's input-parsing behavior for its own commands, I would say it is rather unusual for a programming language's equivalent read function to do line splicing; for example, C source code also supports backslash line continuations, but fread and friends do not do any such magic. I also can't immediately think of another language with a standard input-reading function that does this. – dhag Oct 21 '20 at 15:34
  • @G-ManSays'ReinstateMonica' I think the the surprise of backslash+newline is more about how source code editors/viewers may not always be configured with enough visual cues to help readers check if a line ends in whitespace. – Moh Sep 16 '23 at 00:40
  • Demo: demo() { backslash='\' newline=$'\n'; bash -c "echo wrapped line with $backslash$1$newline date"; }; demo ''; demo ' ' – Moh Sep 16 '23 at 00:41
  • From a reader's perspective, it can be hard to differentiate between echo wrapped line with \ followed by a new line, compared to echo wrapped line with \ followed by a new line! – Moh Sep 16 '23 at 00:41
  • @Moh: While continuing a line with a backslash is treacherous because whitespace after the backslash breaks it, I don’t see how this conversation is about that. – G-Man Says 'Reinstate Monica' Sep 17 '23 at 04:15
  • @G-ManSays'ReinstateMonica': Apologies if I went off-topic. I guess the surprise factor really depends on the context that backslash+newline occurs in. I think that I accidentally conflated several different contexts together: viewing script code vs. a script usage of read from interactive user input vs. a script usage of read for parsing command output – Moh Sep 17 '23 at 16:21
  • IIUC, neither read nor read -r can accept multiple lines due to the default newline delimiter, and read -rd '' seems to work well to accept multiple lines from interactive user input as long as the user knows how to send a null character (e.g. C-v C-@). So I guess the most unintuitive/surprising usage is read -d '' which is probably something to avoid 99% of the time and can lead to surprising results if a user copy-pastes multi-line input from an editor with unintentional invisible whitespace between the backslash+newline combo – Moh Sep 17 '23 at 16:34
  • @Moh: It may be true that read -d '' should probably be avoided for interactive user input.  Did you notice that, in the question, it is used to read from a pipe?  Specifically, a pipe from find … -print0, which uses null characters as record separators. – G-Man Says 'Reinstate Monica' Sep 25 '23 at 08:11
20

The -r option prevents backslash escapes from being interpreted. Here's an example:

Assume there's a file with this content:

ngRTM6hNqgziZcqCcEJN7bHAP9a1GeMs\
Ni3EAX1qvogWpRIPE3oagJL6nwl\QQW9y
bjJHyaVBrUcyZOY5U4h9QHnpEPqg\\\\\\\\\Q9Fk
iNOvAyBTAcN5n1uwR4GvRfAGUbPWiXax\n
cqGPPStH3gaWolrfVAlMtoWiSuLa7GzQ\n\n\n
EnO04N1nEkpWbfXRxrtYNqCZDpF\trQIXS
$ while read line; do echo $line; done < tempfile
ngRTM6hNqgziZcqCcEJN7bHAP9a1GeMsNi3EAX1qvogWpRIPE3oagJL6nwlQQW9y
bjJHyaVBrUcyZOY5U4h9QHnpEPqg\\\\Q9Fk
iNOvAyBTAcN5n1uwR4GvRfAGUbPWiXaxn
cqGPPStH3gaWolrfVAlMtoWiSuLa7GzQnnn
EnO04N1nEkpWbfXRxrtYNqCZDpFtrQIXS
$ while read -r line; do echo $line; done < tempfile
ngRTM6hNqgziZcqCcEJN7bHAP9a1GeMs\
Ni3EAX1qvogWpRIPE3oagJL6nwl\QQW9y
bjJHyaVBrUcyZOY5U4h9QHnpEPqg\\\\\\\\\Q9Fk
iNOvAyBTAcN5n1uwR4GvRfAGUbPWiXax\n
cqGPPStH3gaWolrfVAlMtoWiSuLa7GzQ\n\n\n
EnO04N1nEkpWbfXRxrtYNqCZDpF\trQIXS
muru
  • 72,889
aderchox
  • 691
5

The Bash man page's section about read states that, by default...

The backslash character (\) may be used to remove any special meaning for the next character read and for line continuation.

but, if you pass -r, then

Backslash does not act as an escape character.  The backslash is considered to be part of the line.  In particular, a backslash-newline pair may not then be used as a line continuation.

A little thought suggests that the only possible "special meaning" they could be talking about is the character serving as a delimiter. And sure enough, without -r, you can backslash-escape a delimiter or a newline, but with -r, you can't, and backslashes just get interpreted as literal backslashes:

$ read -d 'x'    var1 <<< 'There was once \
a curious Uni\x user.xHe did a little test.'
$ echo "$var1"
There was once a curious Unix user.
$ read -d 'x' -r var2 <<< 'There was once \
a curious Uni\x user.xHe did a little test.'
$ echo "$var2"
There was once \
a curious Uni\
Mark Amery
  • 3,000