El escenario clásico con precedencia de operador, tiene una línea como:
(cd ~/screenshots/ && ls screenshot* | head -n 5)
Y no sabes si está analizado ((A && B) | C)
o (A && B | C)
...
La documentación casi oficial que se encuentra aquí no enumera la tubería en la lista, por lo que no puedo simplemente verificar en la tabla.
Además, en bash, (
no es solo para cambiar el orden de las operaciones, sino que crea una subshell , por lo que no estoy 100% seguro de que estas líneas sean el equivalente de la línea anterior:
((cd ~/screenshots/ && ls screenshot*) | head -n 5)
En términos más generales, ¿cómo saber el AST de una línea bash? En python tengo una función que me da el árbol para que pueda verificar fácilmente el orden de operación.
|
es solo un conector que se ajusta a la salida estándar LHS a la entrada estándar RHS. Entonces, si elcd
comando en su ejemplo falla, no enviaría ninguna salidahead
, perohead
de hecho aúnexecute
analizaría la nada y no devolvería ninguna salida.bash
está usando un analizador yacc no algo ad-hoc; si lo ejecutayacc -v
, obtendráy.output
una gramática agradable que lo muestra&&
y||
combina listas, y las listas finalmente se componen de tuberías (y no al revés); tl; dr;A && B | C
es lo mismo queA && { B | C; }
, como se esperaba. No asuma ningún orden de ejecución entreB
yC
; Los comandos en una tubería se ejecutan en paralelo .[...]
pruebas y$((...))
evaluaciones aritméticas; en particular,||
y&&
como se usa con la lista de comandos en el lenguaje de shell tiene la misma precedencia , a diferencia de los operadores respectivos enC
o en la evaluación aritmética (donde se&&
une más estrechamente que||
).Respuestas:
Esto es equivalente a
(los comandos del grupo de llaves se unen sin una subshell ). Por lo tanto, la precedencia de
|
es mayor (se une más fuerte) que&&
y||
. Es decir,y
siempre significa que sólo salida de B es que debe darse a C . Puede usar
(...)
o{ ... ; }
para unir comandos como una sola entidad para la desambiguación si es necesario:Puede probar esto usando algunos comandos diferentes. Si tu corres
entonces obtendrás
atrás:
tr a-z A-Z
mayúsculas su entrada , y puede ver que soloecho world
se canalizó en él, mientrasecho hello
realizó por sí solo.Esto se define en la gramática de la cáscara , aunque no está terriblemente claro: la
and_or
producción (para&&
/||
) se define para tener aapipeline
en su cuerpo, mientras quepipeline
solo contienecommand
, que no contieneand_or
, solo lacomplete_command
producción puede alcanzarand_or
, y solo existe en el nivel superior y dentro de los cuerpos de construcciones estructurales como funciones y bucles.Puede aplicar manualmente esa gramática para obtener un árbol de análisis para un comando, pero Bash no proporciona nada en sí. No conozco ningún shell que vaya más allá de lo que se usa para su propio análisis.
La gramática de shell tiene muchos casos especiales definidos solo semi-formalmente y puede ser una misión acertada. Incluso Bash mismo a veces se ha equivocado , por lo que los aspectos prácticos y el ideal pueden ser diferentes.
Hay analizadores externos que intentan hacer coincidir la sintaxis y producir un árbol, y de ellos recomendaré ampliamente Morbig , que intenta ser el más confiable.
fuente
TL; DR : separadores de listas como
;
.&
,&&
y||
decida el orden de análisis.El manual de bash nos dice:
O cómo la wiki de Bash Hacker lo expuso sucintamente
Por lo tanto,
cd ~/screenshots/ && ls screenshot* | head -n 5
hay una tubería,ls screenshot* | head -n 5
y un comando simplecd ~/screenshots/
. Tenga en cuenta que de acuerdo con el manualPor otro lado,
(cd ~/screenshots/ && ls screenshot*) | head -n 5
es diferente: tiene una tubería: a la izquierda hay un subshell y a la derecha tienehead -n 5
. En este caso, usando la notación de OP sería(A && B) | C
Tomemos otro ejemplo:
Aquí tenemos una lista
<pipeline1> && <pipeline2>
. Como sabemos que el estado de salida de la tubería es el mismo que el del último comando yfalse
devuelve el estado negativo, también conocido como falla,&&
no se ejecutará en el lado derecho.Aquí la tubería izquierda tiene un estado de salida exitoso, por lo que se ejecuta la tubería derecha y vemos su salida.
Tenga en cuenta que la gramática de shell no implica el orden de ejecución real. Para citar una de las respuestas de Gilles :
Y del manual bash:
Sobre la base de que en
cd ~/screenshots/ && ls screenshot* | head -n 5
elcd ~/screenshots/
se ejecutaría en primer lugar,ls screenshot* | head -n 5
si el comando anterior tiene éxito, perohead -n 5
puede ser un primer proceso dio lugarls
ya que están en una tubería.fuente
cd ~/screenshots/ && ls screenshot*
ohead -n 5
" Bueno, sí, ese es el caso((cd ~/screenshots/ && ls screenshot*) | head -n 5)
. Pero no dice nada sobre el caso ambiguocd ~/screenshots/ && ls screenshot* | head -n 5
que parece ser el punto de la pregunta (con o sin paréntesis).cd ~/screenshots/ && ls screenshot*
tendría que procesarse primero porque las&&
listas son más altas en el orden de precedencia (basado en la respuesta de l0b0, al menos). ¿Dónde crees que debería mejorar la respuesta?(cd && ls | head)
y((cd && ls) | head)
, podría ser bueno ser explícito sobre a qué te refieres.Aquí es donde se especifica en
bash(1)
:Entonces,
&&
separa las tuberías.fuente
Podrías intentarlo
echo hello && echo world | less
. Verá que|
tiene mayor prioridad (una tubería de comandos es un comando). Para su segundo ejemplo, NO es lo mismo. Sin embargo, debido a quecd
no tiene salida, no verá ninguna diferencia, vía ether.fuente