1. ホーム
  2. linux

シェルのコマンドの前の「!」の意味は?

2023-10-22 17:27:41

質問

エクスクラメーションマークで始まるシェルコマンド(シェルスクリプトの一部)は何のためにあるのですか?

具体的な例です。

foo.shで。

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

スペースがないとエクスクラメーションマークが履歴の置き換えに使われるのは知っているし ! <expression> によると のマニュアルページ は " を評価するために使用することができます。 expr が false ならば真 を評価することができます。しかし、例のコンテキストでは、それは私には意味をなさない。

どのように解決するのですか?

TL;DR: これは、単に set -e フラグを使用する特定の行でバイパスしているだけです。


にaddを追加する。 hek2mgl さんの正しい有用な回答 .

ありますね。

set -e
! command

Bashリファレンスマニュアル → パイプライン を記述します。

パイプラインの各コマンドは、それ自身のサブシェルで実行されます。パイプラインの終了ステータスは、パイプラインの最後のコマンドの終了ステータス (...) です。 予約語 '!' がパイプラインの前にある場合、終了ステータスは終了ステータスの論理的否定となります。 となります。シェルはパイプラインの全てのコマンドが終了するのを待ってから値を返します。

これはつまり ! をコマンドの前に置くことは、そのコマンドの終了ステータスを否定していることになります。

$ 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 が 0 以外のステータスを返した場合、スクリプトは終了し、次の command2 .

しかし、興味深い点として、以下のように記述されていることもあります。 4.3.1 集合ビルトイン :

-e

パイプライン (パイプライン参照) が、単一の単純コマンド (単純コマンド参照)、リスト (リスト参照)、複合コマンド (複合コマンド参照) からなる場合、0 以外のステータスを返したら直ちに終了します。 シェルが終了しない 失敗したコマンドが while または until キーワードの直後のコマンドリスト、 if 文のテストの一部、最後の && または || に続くコマンドを除く && または || リストで実行されるコマンドの一部、最後のコマンドを除くパイプラインのコマンド、 または でコマンドの戻りステータスが反転している場合、! . サブシェル以外の複合コマンドが、-e を無視している間にコマンドが失敗したために 0 以外の状態を返した場合、シェルは終了しません。ERRのトラップが設定されている場合、シェルが終了する前に実行されます。


これらをすべて考慮した上で、あるときは

set -e
! command1
command2

何をしているかというと、バイパスするために set -e フラグを command1 . なぜ?

  • もし command1 が正しく実行されれば、0 のステータスを返します。 ! はそれを否定しますが set -e によって終了を引き起こすことはありません。なぜなら、それは上記のように!
  • もし command1 が失敗すると、0 以外のステータスを返します。 ! はそれを否定するので、その行は結局ゼロのステータスを返し、スクリプトは通常通り続行されます。