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.
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.
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.
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.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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With