在 shell 命令之前的! 是什么意思?

Shell 命令(shell 脚本的一部分)以叹号开头的用途是什么?

具体例子:

在 foo.sh 中:

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

我知道如果没有这个空间,叹号就会被用来替换历史记录,而根据 手册! <expression>可以被用来评估“ 如果 expr 为 false,则为 True”。但是在示例上下文中,这对我来说没有意义。

9557 次浏览

如果您不希望脚本在这两种情况下都失败、错误或命令成功,您也可以使用以下替代方法:

set -e
docker stop foo || true

布尔 或者是真的使管道始终以 0作为返回值。

DR: 这只是在您使用它的特定行中绕过 set -e标志。


Hek2mgl 的正确和有用的答案添加 add。

你有:

set -e
! command

Bash 参考手册→管道 描述:

管道中的每个命令都在其自己的子 shell 中执行。管道的退出状态是管道中最后一个命令(...)的退出状态。如果保留字“ !”在管道之前,退出状态就是退出状态的逻辑非如上所述。Shell 在返回值之前等待管道中的所有命令终止。

这意味着命令前面的 !正在否定命令的退出状态:

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

或者:

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

退出状态在许多方面都很有用。在脚本中,与 set -e一起使用时,只要命令返回非零状态,脚本就会退出。

因此,当你有:

set -e
command1
command2

如果 command1返回非零状态,则脚本将结束,不会继续到 command2

然而,在 4.3.1布景中还有一个有趣的地方值得一提:

E

如果管道(可能包含单个简单命令(参见简单命令)、列表(参见列表)或复合命令(参见复合命令)返回非零状态,则立即退出。如果失败的命令是紧跟在 while 或 until 关键字之后的命令列表的一部分,是 if 语句中的测试的一部分,是在 a & & 或 | | 列表中执行的任何命令的一部分(最后 & & 或 | | 之后的命令除外) ,是管道中的任何命令的一部分(最后一个命令除外) ,或者是 如果命令的返回状态被反转!。如果子 shell 以外的复合命令由于忽略 -e 时命令失败而返回非零状态,则 shell 不会退出。如果对 ERR 设置了陷阱,则在 shell 退出之前执行。


考虑到所有这些因素,当你有:

set -e
! command1
command2

您所做的是绕过 command1中的 set -e标志。为什么?

  • 如果 command1运行正常,它将返回零状态。!将否定它,但 set -e不会触发一个退出,因为它来自一个返回状态倒置!如上所述。
  • 如果 command1失败,它将返回非零状态。!将否定它,因此该行最终将返回一个零状态,脚本将继续正常运行。