Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does the bash read -d '' do?

What does a -d '' do in a bash read command? The example is directly from a previous SO. From the usage printed by the read command, it says that the -d option defines the delimiter for splitting words in a line. What does an empty delimiter do?

read -d '' sql << EOF
select c1, c2 from foo
where c1='something'
EOF

echo "$sql"

I know by experimenting that with it the variable is assigned the multiple lines. Without it, only the first line is assigned. It seems hard to explain this behavior based on the usage text.

like image 864
minghua Avatar asked Aug 14 '19 15:08

minghua


People also ask

What does read do in shell?

The read command reads one line from standard input and assigns the values of each field in the input line to a shell variable using the characters in the IFS (Internal Field Separator) variable as separators.

What does read do in Linux?

The Linux read command is used to read the contents of a line into a variable. This is a built-in command for Linux systems. Therefore, we do not need to install any additional tools. It is an easy tool to take user input when creating a bash script.


2 Answers

read -d changes the character that stops the read from the default newline to the first character of the following argument.

The important thing to understand is that bash uses C strings, which are terminated by literal NULs. Thus, when the following argument is '', the first (and only) character is the NUL terminating it; thus, when the shell dereferences the char* to get the first character it points to, it gets a NUL.


Now, when you redirect a heredoc with <<EOF, that document won't actually have any NULs in it -- so how does your code work?

The answer is that your code expects the read operation to fail. Even when it fails, read still populates its destination variable; so if you don't have a terminating delimiter, read has a nonzero exit status... but it still puts all the data you wanted to collect in the variable anyhow!

For a version that doesn't trigger set -e errors, consider checking whether the destination variable is empty after the read is complete:

{ IFS= read -r -d '' string || [[ $string ]]; } <<'EOF'
...string goes here...
EOF

What are the changes we made?

  • IFS= prevents leading or trailing whitespace (or other characters, should IFS have been redefined) from being stripped.
  • read -r prevents content with backslash literals from being mangled.
  • || [[ $string ]] means that if read reports a failure, we then check whether the string was populated, and still consider the overall command a success should the variable be non-empty.
like image 160
Charles Duffy Avatar answered Sep 21 '22 14:09

Charles Duffy


In bash read builtin empty string delimiter -d '' behaves same as using delimiter as a NUL byte or $'\0' (as defined by ANSI C-quoted string) or in hex representation 0x0.

-d '' specifies that each input line should be delimited by a NUL byte. It means that input string is read up to the immediate next NUL character in each invocation of read.

Usually it is used with IFS= as:

IFS= read -r -d ''

for trimming leading and trailing whitespaces in input.

A common example of processing NUL delimited input is:

while IFS= read -r -d '' file; do
    echo "$file"
done < <(find . -type f -print0)
  • find command is printing files in current directory with NUL as the delimiter between each entry.
  • read -d '' sets \0 as delimiter for reading one entry at a time from output of find command.

Related: Why ‘read’ doesn’t accept \0 as a delimiter in this example?

like image 35
anubhava Avatar answered Sep 19 '22 14:09

anubhava