read -r -d '' TMP <<<'hello'
successfully writes "hello" to $TMP
, yet the exit status is 1
.
Is this expected/intended? Should I just put !
at the start of that line to invert the exit code if I want to to succeed when it succeeds?
N.B. the above example is simplified. Here's the full thing:
IFS='' read -r -d '' SLACK_PAYLOAD <<'JSON'
{
"channel": $channel,
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": $message
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": $changelog
}
}
]
}
JSON
I'm mostly using read
because I want multiple lines.
To be even more clear, my script is using
#!/usr/bin/env -S bash -eio pipefail
It appears there are some subtle differences with zsh
which I'm not really opposed to (I use it as my shell but not for scripts), but my script is already written for bash.
CodePudding user response:
read
expects the line to end with the configured delimiter and errors out if it's missing. You can see this even when not using -d
if you have it read input that's missing the usual newline at EOF.
How can we pass input without it? Well, <<<
automatically adds a newline, so that won't work. We can omit it by using
printf
, which doesn't add newlines the way<<<
(orecho
) does; and- process substitution to pass
printf
's output toread
without creating a subshell. If we used a pipeline likeprintf | read
thenread
would run in a subshell and the parent shell wouldn't see the assignment toTMP
.
That looks like:
bash❯ read -r TMP < <(printf 'hello')
bash❯ echo $?
1
Now when you use -d ''
, read
looks for a \0
(NUL character) delimiter. Of course there is no \0
at the end of 'hello'
. So let's add one! If we do then it succeeds:
bash❯ read -r -d '' TMP < <(printf 'hello')
bash❯ echo $?
1
bash❯ read -r -d '' TMP < <(printf 'hello\0')
bash❯ echo $?
0
Interestingly, in zsh process substitution isn't needed:
zsh❯ read -r -d '' TMP <<< $'hello'
zsh❯ echo $?
1
zsh❯ read -r -d '' TMP <<< $'hello\0'
zsh❯ echo $?
0
If your intention is to set a variable to a large multiline string there's no need for read
. Strings can span multiple lines:
SLACK_PAYLOAD='
{
"channel": $channel,
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": $message
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": $changelog
}
}
]
}
')
Or if you like the heredoc syntax, this version with cat
would also work:
SLACK_PAYLOAD="$(cat <<'JSON'
{
"channel": $channel,
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": $message
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": $changelog
}
}
]
}
JSON
)"
CodePudding user response:
Is this expected/intended?
Yes.
Exit Status: The return code is zero, unless end-of-file is encountered, read times out (in which case it's greater than 128), a variable assignment error occurs, or an invalid file descriptor is supplied as the argument to -u.
(from read --help
, emphasis mine)
In your case, it seems like you don't need read
at all, you could just do
TMP='hello'