What does the bash read -d '' do?
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 offind
command.
Related: Why ‘read’ doesn’t accept \0 as a delimiter in this example?
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 ifread
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.