Uso de Bang (!) en bash

Uso de Bang (!) en bash

Estoy leyendo el código fuente de bash y elGramática BNFpara bash sería:

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

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

¿Significa esto que !el comando también es una especie de tubería?

! lsFunciona, sin embargo, es lo mismo que ls.

! time lsFunciona también.

Eso es bastante diferente a |la pipa.

¿Cómo usarlo !en bash? ¿Es una pipa?

Respuesta1

Del manual de bash: "Si la palabra reservada! precede a una tubería, el estado de salida de esa tubería es la negación lógica del estado de salida"

Estás leyendo mal la gramática. Lo que dice la gramática es que puedes poner un ! frente a un oleoducto, no reemplazar | con un !.

Respuesta2

El signo de exclamación simplemente invierte lógicamente el código de retorno del comando/canalización (ver, por ejemplomanual de 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

El código de retorno de una tubería es (generalmente) solo el código de retorno del último comando, por lo que bang invierte eso:

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

Da la casualidad de que no puedo ver ese archivo BNF en la distribución fuente de Bash, y la gramática citada no es exactamente precisa, ya que Bash acepta múltiples explosiones desde Bash 4.2 (lanzado en 2011):

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

Sin embargo, eso no es estándar y, por ejemplo, zsh y Dash lo croan.

Respuesta3

Definición de una tubería que seráunoo más comandos significa que un solo comando también es una tubería, aunque en realidad no involucra una tubería. El beneficio es que, !como operador de negación, no es necesario definirlo por separado para comandos y canalizaciones; sólo es necesario definirlo como aplicable a una tubería.

En ! cmd1 | cmd2, !niega el estado de salida de todo el proceso, no solo del comando único cmd1. El estado de salida de una canalización, de forma predeterminada, es el estado de salida del comando situado más a la derecha.


Asimismo, unlistaes una tubería más unida por ;, &, &&, o ||. Por lo tanto, una única canalización también es una lista y un único comando también es una lista. Luego, cuando un comando como ifse define tomando una lista entre las palabras clave ify then, esto incluye automáticamente comandos únicos y canalizaciones únicas como parte de la definición de un comando.

  • Una lista que consta de dos canalizaciones (una de las cuales solo consta de un comando):

    if IFS= read -r response && echo "$response" | grep foo; then
    
  • Una lista que consta de una única canalización:

    if echo "$foo" | grep foo; then
    
  • Una lista que consta de una sola canalización (que a su vez contiene solo un comando):

    if true; then
    

Respuesta4

Un par de puntos para agregar a lo que dijeron las otras respuestas:

  • Como lo señaló (indirectamente)la respuesta de chepner, el !operador se define como un prefijo opcional del <pipeline_command>elemento sintáctico, en lugar de <command>. Esto tiene la consecuencia de que no se puede decir

      cmd1 | ! cmd2
    

    o

    ! cmd1 | ! cmd2
    

    Sólo puede negar el estado de salida de un "canal completo". Como señaló Chepner, una "canalización" puede ser un comando único, por lo que puedes hacer cosas como

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

    pero eso es una tontería. El !antes cmd2no hace nada en absoluto; el !before cmd4afecta solo al valor de $? al final del comando, y los otros dos se pueden eliminar intercambiando el AND y el OR:

      cmd1  ||  cmd2;   cmd3  &&  cmd4
    

    De manera similar, while ! cmdse puede reemplazar con until cmd.

  • A <pipeline>precedida por a !se convierte en a <pipeline_command>, un elemento sintáctico diferente. Por lo tanto, no es válido decir

    ! ! cmd1
    

    a diferencia de la expansión aritmética, donde cosas como y son válidas.$((! ! value))$((! ! ! value))

  • Se le informa quePOSIXdefine la misma gramática, pero utiliza diferentes nombres de elementos. El BNF en la pregunta aparece en POSIX como

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

    ¿Dónde Bangestá a %tokencon el valor '!'?

información relacionada