web-dev-qa-db-ja.com

の意味は何ですか!シェルのコマンドの前に?

質問はタイトルにあります。感嘆符で始まるシェルコマンド(シェルスクリプトの一部)の目的は何ですか?具体例:

foo.sh:

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

スペースがないと、感嘆符が履歴の置換に使用され、 man page に従って! <expression>を使用して、「exprがfalseの場合はtrue 」。しかし、私にとって意味をなさない例のコンテキストでは。

71
sthzg

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


hek2mglの正解および有用な回答 にaddを追加します。

あなたが持っている:

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がゼロ以外のステータスを返す場合、スクリプトは終了し、command2に進みません。

ただし、 4.3.1 The Set Builtin で説明されている興味深い点もあります。

-e

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


次の場合、これらすべてを考慮に入れてください。

set -e
! command1
command2

あなたがしているのは、set -ecommand1フラグをバイパスすることです。どうして?

  • command1が正常に実行されると、ゼロのステータスが返されます。 !はそれを無効にしますが、set -eは、上記のように!で反転された戻りステータスから来るため、終了をトリガーしません。
  • command1が失敗すると、ゼロ以外のステータスを返します。 !はそれを無効にするため、行は最終的にゼロのステータスを返し、スクリプトは通常どおり続行されます。
77
fedorqui

コマンドのエラーまたは成功の両方の場合にスクリプトを失敗させたくない場合は、この代替手段も使用できます。

set -e
docker stop foo || true

ブール値またはtrueを指定すると、パイプラインの戻り値は常に0になります。

23
hek2mgl

「!」 「しない」という意味なので、コマンドの前に配置して「$?」を実行するとこれは1をエコーし​​ます。つまり、成功を意味する0の代わりに失敗を意味します。

1
Les K