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

感嘆符は、コマンド/パイプラインの戻りコードを論理的に反転するだけです(例を参照)。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

実際のところ、Bash ソース配布物にはその BNF ファイルは見当たりません。また、引用された文法は正確ではありません。Bash は、Bash 4.2 (2011 年リリース) 以降、複数の感嘆符を受け入れるようになったためです。

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

ただし、これは標準ではなく、たとえば zsh や Dash では動作しません。

答え3

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

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


同様に、リスト;は、、、、&または&&で結合された 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
    

答え4

他の回答にいくつか追加する点があります:

  • (間接的に)チェプナーの答えでは、演算子は ではなく、構文要素!のオプションの接頭辞として定義されます。このため、次のように言うことはできません。<pipeline_command><command>

      cmd1 | ! cmd2
    

    または

    ! cmd1 | ! cmd2
    

    「パイプライン全体」の終了ステータスを否定することしかできません。chepnerが指摘したように、「パイプライン」は単一のコマンドになることもあるので、次のようなことができます。

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

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

      cmd1  ||  cmd2;   cmd3  &&  cmd4
    

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

  • <pipeline>aの前にaが付くと、aは別の構文要素に!なります<pipeline_command>。したがって、次のように言うのは正しくありません。

    ! ! cmd1
    

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

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

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

    ここで、は値 を持つBangです。%token'!'

関連情報