Uso de Bang (!) no bash

Uso de Bang (!) no bash

Estou lendo o código-fonte do bash e oGramática BNFpara bash seria:

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

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

Isso significa que !o comando também é uma espécie de tubo.

! lsfunciona, porém é o mesmo que ls.

! time lsfunciona também.

Isso é bem diferente do |tubo.

Como usar !no bash? É um cano?

Responder1

Do manual do bash: "Se a palavra reservada! Precede um pipeline, o status de saída desse pipeline é a negação lógica do status de saída"

Você está interpretando mal a gramática. O que a gramática diz é que você pode colocar um ! na frente de um gasoduto, não substitua | com um !.

Responder2

O ponto de exclamação apenas inverte logicamente o código de retorno do comando/pipeline (veja, por exemplo,Manual do 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

O código de retorno de um pipeline é (geralmente) apenas o código de retorno do último comando, então o bang inverte isso:

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

Acontece que não consigo ver o arquivo BNF na distribuição de origem do Bash, e a gramática citada não é exatamente precisa, já que o Bash aceita vários bangs desde o Bash 4.2 (lançado em 2011):

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

Isso não é padrão, e por exemplo, zsh e Dash fazem isso.

Responder3

Definindo um pipeline a serumou mais comandos significa que um único comando também é um pipeline, embora não envolva realmente um pipeline. A vantagem é que, !como operador de negação, não precisa ser definido separadamente para comandos e pipelines; ele só precisa ser definido como aplicável a um pipeline.

Em ! cmd1 | cmd2, !nega o status de saída de todo o pipeline, não apenas do único comando cmd1. O status de saída de um pipeline, por padrão, é o status de saída do comando mais à direita.


Da mesma forma, umlistaé mais um pipeline unido por ;, &, &&ou ||. Assim, um único pipeline também é uma lista e um único comando também é uma lista. Então, quando um comando como ifé definido como uma lista entre as palavras-chave ife then, isso inclui automaticamente comandos únicos e pipelines únicos como parte da definição de um comando.

  • Uma lista que consiste em dois pipelines (um dos quais consiste apenas em um comando):

    if IFS= read -r response && echo "$response" | grep foo; then
    
  • Uma lista que consiste em um único pipeline:

    if echo "$foo" | grep foo; then
    
  • Uma lista que consiste em um único pipeline (que contém apenas um único comando):

    if true; then
    

Responder4

Alguns pontos a acrescentar ao que as outras respostas disseram:

  • Como observado (indiretamente) porresposta de chepner, o !operador é definido como um prefixo opcional para o <pipeline_command>elemento sintático, em vez de <command>. Isto tem a consequência de que você não pode dizer

      cmd1 | ! cmd2
    

    ou

    ! cmd1 | ! cmd2
    

    Você só pode negar o status de saída de um “pipeline inteiro”. Como apontou Chepner, um “pipeline” pode ser um único comando, então você pode fazer coisas como

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

    mas isso é bobagem. O !antes cmd2não faz absolutamente nada; o !antes cmd4afeta apenas o valor de $? no final do comando, e os outros dois podem ser eliminados trocando o AND e o OR:

      cmd1  ||  cmd2;   cmd3  &&  cmd4
    

    Da mesma forma, while ! cmdpode ser substituído por until cmd.

  • A <pipeline>precedido por a !torna-se a <pipeline_command>— um elemento sintático diferente. Portanto, não é válido dizer

    ! ! cmd1
    

    ao contrário da expansão aritmética, onde coisas como e são válidas.$((! ! value))$((! ! ! value))

  • Esteja avisado quePOSIXdefine a mesma gramática, mas usa nomes de elementos diferentes. O BNF na questão aparece no POSIX como

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

    onde Bangestá a %tokencom o valor '!'.

informação relacionada