I understand history expansion in C-shell and BASH, but I don't understand why the exclamation mark on the line with the read
command is being used in this snippet from some source I'm looking at:
#!/bin/bash
set -e
. "$(dirname "$0")/includes.sh"
! read -d '' SOME_VAR <<"EOT"
some ASCII art
EOT
echo -e "\033[95m$SOME_VAR\033[0m"
Why would you negate the return value of the read
command (I think that is its effect, not history expansion)? Are there any possible error conditions, besides out-of-memory? The only one I can think of would be EINTR
, which I think would be an abort condition (SIGINT
or SIGHUP
). And why would you double-quote the starting heredoc
marker (EOT
in this case), and not the ending marker?
It's from a major open source project, so I was guessing the author had some reason for doing this that I haven't been able to discern.
Bash maintains the history of the commands executed in the current session. We can use the exclamation mark (!) to execute specific commands from the history.
In addition to using single quotes for exclamations, in most shells you can also use a backslash \ to escape it.
An exclamation mark starts history expansions in many Unix shells such as bash and tcsh where !! executes the previous command and !* refers to all of the arguments from the previous command.
In programming and scripting languages, the exclamation mark is used as "not." For example, "! =" also means not equal. See our operator definition for further information. Used to help identify a nonexecutable statement.
The set -e
command tells bash to exit if a pipeline returns a non-zero exit status (basically if a command fails).
The !
negates the exit status, but it also inhibits the effect of set -e
. The idea is that if a command is being executed as part of a condition, you don't want to terminate the shell. If it's preceded by !
, the shell effectively assumes that it's being executed as a condition (even if the result is ignored).
Quoting the bash manual:
The shell does not exit if the command that fails is part of the command list immediately following a 'while' or 'until' keyword, part of the test in an 'if' statement, part of any command executed in a '&&' or '||' list except the command following the final '&&' or '||', any command in a pipeline but the last, or if the command's return status is being inverted with '!'.
https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
(You're correct that the !
has nothing to do with history substitution in this context.)
The return code for the read
built-in 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.
The relevant case here is end-of-file. Redirecting the input of read
using <<EOT
(and disabling normal termination using -d ''
) means that the read
command will encounter an end-of-file, which would cause it to return a non-zero status. The !
prevents this from aborting the script (but the value is still assigned to $SOME_VAR
).
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