What's the point! before a command in a shell?

The question is in the title. What is the purpose of a shell command (part of a shell script) starting with an exclamation mark? Specific example:

In foo.sh:

#!/usr/bin/env bash set -e ! docker stop foo ! docker rm -f foo # ... other stuff 

I know that without a space, an exclamation mark is used to replace the story and ! <expression> ! <expression> according to the man page for evaluating "True if the expression is false." But in the context of the example, this does not make sense to me.

+70
linux unix bash shell sh
Nov 13 '17 at 9:47 on
source share
3 answers

TL; DR: this is just a workaround for the set -e flag on the specific line where you use it.




Adding the correct and useful answer to hek2mgl .

You have:

 set -e ! command 

Bash Reference Guide → Piping describes:

Each command in the pipeline is executed in its own subshell. The pipeline exit status is the exit status of the last command in the pipeline (...). If the reserved word '! precedes the pipeline, exit status is a logical negation of exit status , as described above. The shell waits for all commands in the pipeline to complete before returning a value.

This means that ! preceding the command denies its exit status:

 $ echo 23 23 $ echo $? 0 # But $ ! echo 23 23 $ echo $? 1 

Or:

 $ echo 23 && echo "true" || echo "fail" 23 true $ ! echo 23 && echo "true" || echo "fail" 23 fail 

Exit status is useful in many ways. In the script used with set -e , the script is called when the command returns a non-zero status.

Thus, if you have:

 set -e command1 command2 

If command1 returns a non-zero status, the script will end and not go to command2 .

However, there is also an interesting point mentioned in 4.3.1 Set Builtin :

-e

Exit immediately if the pipeline (see "Pipelines"), which can consist of one simple command (see "Simple commands"), a list (see Lists), or a compound command (see compound commands), returns a non-zero status. The shell does not exit if the failed command is part of the list of commands immediately after some time or until the keyword, part of the test in the if statement, is part of any command executed in && & &; or || the list, with the exception of the command following the final && or ||, any command in the pipeline, but the last, or if the return status of the commands is flipped with! . If a compound command other than a subshell returns a nonzero status because the command was unsuccessful and -e was ignored, the shell did not exit. The trap in the ERR, if set, runs before the shell exits.




Given all this, when you have:

 set -e ! command1 command2 

What you do is bypass the set -e flag in command1 . Why?

  • If command1 works correctly, it will return a null status. ! will deny it, but set -e will not cause an exit due to the fact that it returns from the return state inverted with !, as described above.
  • If command1 does not work, it will return a non-zero status. ! will deny this, so the string will return a null status, and the script will continue normally.
+76
Nov 13 '17 at 13:34 on
source share

If you do not want the script to crash in both cases, a command error or success, you can also use this alternative:

 set -e docker stop foo || true 

Boolean or true makes the pipeline always 0 as the return value.

+23
Nov 13 '17 at 10:08
source share

"!" It means no, so when you place it before the command and run "$?" It will echo 1. Which means it failed instead of 0, which means success.

+1
Nov 13 '17 at 17:24
source share



All Articles