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?
! ls
Funciona, sin embargo, es lo mismo que ls
.
! time ls
Funciona 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 if
se define tomando una lista entre las palabras clave if
y 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 decircmd1 | ! 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
!
antescmd2
no hace nada en absoluto; el!
beforecmd4
afecta 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 ! cmd
se puede reemplazar conuntil 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
Bang
está a%token
con el valor'!'
?