Uso de Bang (!) no bash

2

Estou lendo o código-fonte bash, e a gramática BNF para o 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 é um tipo de tubo.

! ls funciona, no entanto, é o mesmo que ls .

! time ls também funciona.

Isso é bem diferente de | pipe.

Como usar ! no bash? É um cachimbo?

    
por frams 25.11.2017 / 14:39

4 respostas

6

No 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!.

    
por 25.11.2017 / 14:52
4

O ponto de exclamação apenas inverte logicamente o código de retorno do comando / pipeline (ver, 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
    
por 25.11.2017 / 14:49
2

Definir um pipeline como um ou mais comandos significa que um único comando também é um pipeline, embora não envolva um pipe. O benefício é que ! como um operador de negação não precisa ser definido separadamente para comandos e pipelines; só precisa ser definido como aplicado a um pipeline.

Em ! cmd1 | cmd2 , o ! nega o status de saída do pipeline inteiro, não apenas o ú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, uma lista é mais um conjunto de pipelines unidos 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 tendo uma lista entre as palavras-chave if e then , isso inclui automaticamente comandos únicos e pipelines simples 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
    
por 26.11.2017 / 16:23
1

Alguns pontos para adicionar ao que as outras respostas disseram:

  • Conforme observado (indiretamente) pela resposta da chepner , o operador ! é definido como um prefixo opcional para o elemento <pipeline_command> sintático, em vez de <command> . Isso 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 chepner apontou, um "pipeline" pode ser um único comando, então você pode fazer coisas como

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

    mas isso é bobo. O ! antes de cmd2 não faz nada; o ! antes de cmd4 afeta 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 ! cmd pode ser substituído por until cmd .

  • Um <pipeline> precedido por um ! torna-se um <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 $((! ! value)) e $((! ! ! value)) são válidos.

  • Esteja ciente de que POSIX define 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
    

    em que Bang é um %token com o valor '!' .

por 30.11.2017 / 19:14