Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is exclamation mark at beginning of line doing in this BASH snippet?

Tags:

bash

shell

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.

like image 200
webstackdev Avatar asked Mar 16 '18 00:03

webstackdev


People also ask

What does exclamation mark do in bash?

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.

How do I escape exclamation mark in bash?

In addition to using single quotes for exclamations, in most shells you can also use a backslash \ to escape it.

What does the exclamation point mean in Unix?

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.

What does exclamation mark mean in code?

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.


1 Answers

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).

like image 65
Keith Thompson Avatar answered Nov 10 '22 06:11

Keith Thompson