BashでのBang(!)の使い方

BashでのBang(!)の使い方

bashのソースコードを読んでいます。BNF構文Bashの場合:

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

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

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

! ls動作しますが、と同じですls

! time lsまた動作します。

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

!Bashでどのように使用しますか?パイプですか?

答え1

Bashのマニュアルでは、「予約語!がパイプの前に来ると、そのパイプの終了状態は終了状態の論理的否定です。」

文法を誤って読みましたね。構文で言うのは、|を前にパイプと一緒に入れることができるということです。

答え2

感嘆符は、コマンド/パイプの戻りコードを論理的に反転します(例:バッシュマニュアル):

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

結局のところ、BashソースディストリビューションではBNFファイルを見ることができず、Bashは2011年にリリースされたBash 4.2以降の複数の感嘆符を受け入れるため、引用された構文は完全に正確ではありません。

if ! ! true ; then echo 'this prints too' ; fi

しかし、これは標準ではありません。例えば、zsh と Dash はこれに関してはうまくいきません。

答え3

パイプを次のように定義します。一つあるいは、複数のコマンドは、実際にパイプが含まれていなくても、単一のコマンドがパイプでもあることを意味します。利点は、!否定演算子としてコマンドとパイプに対して別々に定義する必要がなく、パイプに適用するためにのみ定義する必要があることです。

では、個々のコマンドだけでなく、パイプライン全体の終了ステータスをキャンセル! cmd1 | cmd2します。デフォルトでは、パイプの終了状態は一番右のコマンドの終了状態です。!cmd1


同様に、リスト;、、、&またはで&&接続された別のパイプです||。したがって、単一パイプもリストであり、単一コマンドもリストです。その後、このコマンドがtakeとキーワードifの間のリストとして定義されると、コマンド定義の一部として単一のコマンドと単一のパイプが自動的に含まれます。ifthen

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

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

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

    if true; then
    

答え4

他の答えに基づいたいくつかの追加:

  • (間接的に)指摘したようにチェフナーの答え代わりに、演算子は!構文要素のオプションの接頭辞として定義されます。これはあなたが言うことができない結果をもたらすでしょう<pipeline_command><command>

      cmd1 | ! cmd2
    

    または

    ! cmd1 | ! cmd2
    

    「フルパイプライン」の終了状態のみを無効にできます。 Cheppnerが指摘したように、「パイプライン」は単一のコマンドになる可能性があるため、次のことができます。

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

    しかし、これは愚かなことです。 beforeは何も!cmd2ません。 beforeはコマンドの最後の値!にのみ影響し、ANDとORを交換して残りの2つを削除できます。cmd4$?

      cmd1  ||  cmd2;   cmd3  &&  cmd4
    

    同様while ! cmdに置き換えることができますuntil cmd

  • <pipeline>a が先行する A は!-<pipeline_command>他の構文要素になります。だから言う

    ! ! cmd1
    

    のようなものが有効な算術拡張とは異なります。$((! ! value))$((! ! ! value))

  • 参考にしてくださいPOSIX同じ構文を定義しますが、異なる要素名を使用します。問題のBNFは、POSIXでは次のように表示されます。

    pipeline         :      pipe_sequence
                     | Bang pipe_sequence
    
    pipe_sequence    :                             command
                     | pipe_sequence '|' linebreak command
    

    ここでBangaの%token値はです'!'

関連情報