web-dev-qa-db-ja.com

bashでのBang(!)の使用

私はbashのソースコードを読んでおり、bashの BNF文法 は次のようになります。

<pipeline_command> ::= <pipeline>
                    |  '!' <pipeline>
                    |  <timespec> <pipeline>
                    |  <timespec> '!' <pipeline>
                    |  '!' <timespec> <pipeline>

<pipeline> ::=
          <pipeline> '|' <newline_list> <pipeline>
       |  <command>

これは、!コマンドも一種のパイプであることを意味しますか?.

! lsは機能しますが、lsと同じです。

! time lsも機能します。

これは|パイプとはかなり異なります。

Bashで!を使用する方法パイプですか?

5
frams

Bashマニュアルから:「予約語!がパイプラインの前にある場合、そのパイプラインの終了ステータスは終了ステータスの論理否定です」

あなたは文法を誤解しています。文法が言っているのは、!パイプラインの前で、置き換えないでください。とともに !。

7
Johan Myréen

感嘆符は、コマンド/パイプラインの戻りコードを論理的に逆にします(例: Bashのマニュアル を参照):

if true ;    then echo this prints ; fi
if ! false ; then echo this also prints ; fi
if ! true ;  then echo this does not print ; fi

パイプラインの戻りコードは(通常)直前のコマンドの戻りコードに過ぎないため、強打すると次のようになります。

if ! true | false ; then echo again, this also prints ; fi
4
ilkkachu

パイプラインをone以上のコマンドとして定義すると、実際にはパイプを含まないものの、単一のコマンドもパイプラインになります。利点は、否定演算子としての!をコマンドとパイプラインに個別に定義する必要がないことです。パイプラインに適用するものとして定義する必要があるだけです。

! cmd1 | cmd2では、!は、単一のコマンドcmd1だけでなく、パイプライン全体の終了ステータスを無効にします。パイプラインの終了ステータスは、デフォルトでは、右端のコマンドの終了ステータスです。


同様に、listは、;&&&、または||で結合されたもう1つのパイプラインです。したがって、単一のパイプラインもリストであり、単一のコマンドもリストです。次に、ifのようなコマンドがifthenキーワードの間のリストをとるように定義されている場合、これは自動的に単一のコマンドと単一のパイプラインを定義の一部として含みますコマンド。

  • 2つのパイプラインで構成されるリスト(そのうちの1つは1つのコマンドのみで構成されます):

    if IFS= read -r response && echo "$response" | grep foo; then
    
  • 単一のパイプラインで構成されるリスト:

    if echo "$foo" | grep foo; then
    
  • 単一のパイプライン(それ自体が単一のコマンドのみを含む)で構成されるリスト:

    if true; then
    
2
chepner

他の回答が言ったことに追加するいくつかのポイント:

  • chepner’s answer によって(間接的に)記述されているように、_!_演算子は、_<pipeline_command>_ではなく_<command>_構文要素のオプションの接頭辞として定義されます。これはあなたが言うことができないという結果を持っています

    _  cmd1 | ! cmd2
    _

    または

    _! cmd1 | ! cmd2
    _

    「パイプライン全体」の終了ステータスのみを無効にすることができます。 chepnerが指摘したように、「パイプライン」は単一のコマンドにすることができるため、次のようなことができます

    _! cmd1 && ! cmd2; ! cmd3 || ! cmd4
    _

    しかし、それはばかげています。 _!_の前の_cmd2_は、何もしません。 _!_の前の_cmd4_は、コマンドの最後にある_$?_の値にのみ影響し、他の2つはANDとORを交換することで削除できます。

    _  cmd1  ||  cmd2;   cmd3  &&  cmd4
    _

    同様に、_while ! cmd_は_until cmd_に置き換えることができます。

  • _<pipeline>_が前に付いた_!_は、_<pipeline_command>_になります—別の構文要素です。したがって、それは言うことは有効ではありません

    _! ! cmd1
    _

    算術展開とは異なり、$((! ! value))$((! ! ! value))などが有効です。

  • [〜#〜] posix [〜#〜] は同じ文法を定義しますが、異なる要素名を使用することに注意してください。質問のBNFは、POSIXでは次のように表示されます

    _pipeline         :      pipe_sequence
                     | Bang pipe_sequence
    
    pipe_sequence    :                             command
                     | pipe_sequence '|' linebreak command
    _

    ここで、Bangは_%token_の値を持つ_'!'_です。