4

I have a bash script where I'm trying to assign a heredoc string to a variable using read, and it only works if I use read with the -d '' option, I.e.

read -d '' <variable>

script block

#!/usr/bin/env bash

function print_status() {
    echo
    echo "$1"
    echo 
}


read -d '' str <<- EOF
    Setup nginx site-config
    NOTE: if an /etc/nginx/sites-available config already exists for this
    website, this routine will replace existing config with template from
    this script. 
EOF

print_status "$str"

I found this answer on SO which is where I copied the command from, it works, but why? I know the first invocation of read stops when it encounters the first newline character, so if I use some character that doesn't appear in the string the whole heredoc gets read in, e.g.

  • read -d '|' <variable> -- this works
  • read -d'' <variable> -- this doesn't

I'm sure it's simple but what's going on with this read -d '' command option?

7

I guess the question is why read -d '' works though read -d'' doesn't.

The problem doesn't have anything to do with read but is a quoting "problem". A "" / '' which is part of a string (word) simply is not recognized at all. Let the shell show you what is sees / executes:

start cmd:> set -x

start cmd:> echo read -d " " foo
+ echo read -d ' ' foo

start cmd:> echo read -d" " foo
+ echo read '-d ' foo

start cmd:> echo read -d "" foo
+ echo read -d '' foo

start cmd:> echo read -d"" foo
+ echo read -d foo
  • I see what you mean, so by passing the "" read will be looking for an empty string which is technically a non-existent character - so read will read in the whole string. But you still need to pass "" so that the -d argument works - even though you are kind of "tricking" it, thanks – the_velour_fog Jan 26 '16 at 7:52
  • 1
    It's also worth nothing that read -d '' is a bit of a hack. You aren't specifying any character to look for as the end of a line (or possibly you are looking for the null character, ASCII 0, which due to implementation details cannot occur in a string), so read always just reads up to the end of its input, at which point it exits with a non-zero exit status because it never found the requested end-of-line character. This can be an issue if you are expecting read to succeed, such as if you are using set -e. – chepner Jan 27 '16 at 16:55
  • @chepner - in most cases set -e is only used in poorly written shell scripts anyway. – mikeserv Jan 28 '16 at 17:15
  • @mikeserv If switching on -e breaks a shell script. Than it is a poor shell script. I always use -e. – user2059857 Jan 2 at 22:16
1

Your heredoc thing is a lot more simply written like:

print_status(){ paste -sd\\n /dev/null - /dev/null;}

print_status <<\doc
doc body
doc

You dont have to assign all of that to a name. For that matter, more simply still it could be:

print_status(){ printf %s\\n '' "$@" '';}

print_status \
    'line 2' \
    'line 3' and lines 4,5,6

My point is it that I think it is a little over the top to write a bunch of text into your script file which you then instruct the shell to read out to a temporary file (which is generally how here-documents work) and to open that temporary file on some command's stdin if your targeted command is just meant to be the shell all over again reading it back into a variable! See? It's a little redundant, in my opinion.

  • thanks for your feedback, I will give your idea about printf a try because its more portable than a bash echo. To me the extra overhead of heredoc's are worth it, because I can just type normally messages and not have to have shell special characters , like `\` newline continuations etc. Its nice and readable – the_velour_fog Jan 27 '16 at 2:07
  • @the_velour_fog - Then consider the paste at least. Or maybe some stuff with set -v and comments. And anyway its fine as long as youre fully aware of what youre doing and its worth it to you then go ahead, and with my blessing. I only wanted to speak up because quite often people dont consider that stuff - i didnt used to, anyway. – mikeserv Jan 27 '16 at 2:13
  • yes i see what you mean, it looks like you have a couple of patterns that definitely will make the code cleaner so I will give them a try! – the_velour_fog Jan 27 '16 at 2:23

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.