Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
504 views
in Technique[技术] by (71.8m points)

linux - What's the meaning of a ! before a command in the shell?

What is the purpose of a shell command (part of a shell script) starting with an exclamation mark?

Concrete example:

In foo.sh:

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

I know that without the space the exclamation mark is used for history replacements and ! <expression> according to the man page can be used to evaluate "True if expr is false". But in the example context that does not make sense to me.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

TL;DR: This is just by-passing the set -e flag in the specific line where you are using it.


Adding add to hek2mgl's correct and useful answer.

You have:

set -e
! command

Bash Reference Manual → Pipelines describes:

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

This means that ! preceding a command is negating the exit status of it:

$ 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

The exit status is useful in many ways. In your script, used together with set -e makes the script exit whenever a command returns a non-zero status.

Thus, when you have:

set -e
command1
command2

If command1 returns a non-zero status, the script will finish and won't proceed to command2.

However, there is also an interesting point to mention, described in 4.3.1 The Set Builtin:

-e

Exit immediately if a pipeline (see Pipelines), which may consist of a single 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 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 !. If a compound command other than a subshell returns a non-zero status because a command failed while -e was being ignored, the shell does not exit. A trap on ERR, if set, is executed before the shell exits.


Taking all of these into consideration, when you have:

set -e
! command1
command2

What you are doing is to by-pass the set -e flag in the command1. Why?

  • if command1 runs properly, it will return a zero status. ! will negate it, but set -e won't trigger an exit by the because it comes from a return status inverted with !, as described above.
  • if command1 fails, it will return a non-zero status. ! will negate it, so the line will end up returning a zero status and the script will continue normally.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...