O cenário clássico com Precedência de Operador, você tem uma linha como:
(cd ~/screenshots/ && ls screenshot* | head -n 5)
E você não sabe se é analisado ((A && B) | C)
ou (A && B | C)
...
A documentação quase oficial encontrada aqui não lista o pipe na lista, então não posso simplesmente verificar a tabela.
Além disso, no bash, (
não é apenas para alterar a ordem das operações, mas cria um subshell , então não tenho 100% de certeza de que essas linhas são equivalentes à linha anterior:
((cd ~/screenshots/ && ls screenshot*) | head -n 5)
Mais geralmente, como saber o AST de uma linha bash? Em python eu tenho uma função que me dá a árvore para que eu possa facilmente verificar a ordem de operação.
Isso é equivalente a
(as chaves agrupam comandos juntos sem um subshell ). A precedência de
|
é, portanto, maior (vinculações mais apertadas) do que&&
e||
. Aquilo é,e
sempre significa que apenas a saída de B deve ser dada a C. Você pode usar
(...)
ou{ ... ; }
para unir comandos como uma única entidade para desambiguação, se necessário:Você pode testar isso usando alguns comandos diferentes. Se você correr
então você vai conseguir
back:
tr a-z A-Z
maiúsculas sua entrada , e você pode ver que apenasecho world
foi canalizado para ele, enquantoecho hello
passou por conta própria.Isso é definido na gramática do shell , embora não muito claramente: a
and_or
produção (for&&
/||
) é definida para ter aapipeline
em seu corpo, enquantopipeline
apenas contémcommand
, que não contémand_or
- apenas acomplete_command
produção pode alcançarand_or
, e só existe no nível superior e dentro dos corpos de construções estruturais como funções e loops.Você pode aplicar manualmente essa gramática para obter uma árvore de análise para um comando, mas o Bash não fornece nada em si. Não conheço nenhum shell que faça além do que é usado para sua própria análise.
A gramática do shell tem muitos casos especiais definidos apenas semiformalmente e pode ser uma missão e tanto acertar. Até o próprio Bash às vezes errou , então os aspectos práticos e o ideal podem ser diferentes.
Existem analisadores externos que tentam combinar a sintaxe e produzir uma árvore, e desses eu recomendarei amplamente o Morbig , que tenta ser o mais confiável.
TL;DR : separadores de lista como
;
.&
,&&
, e||
decidir a ordem de análise.O manual do bash nos diz:
Ou como o wiki do Bash Hacker colocou sucintamente
Assim,
cd ~/screenshots/ && ls screenshot* | head -n 5
há um pipeline -ls screenshot* | head -n 5
e um comando simplescd ~/screenshots/
. Observe que de acordo com o manualPor outro lado,
(cd ~/screenshots/ && ls screenshot*) | head -n 5
é diferente - você tem um pipeline: à esquerda há subshell e à direita você temhead -n 5
. Neste caso, usando a notação do OP, seria(A && B) | C
Vamos a outro exemplo:
Aqui temos uma lista
<pipeline1> && <pipeline2>
. Como sabemos que o status de saída do pipeline é o mesmo do último comando efalse
retorna um status negativo, também conhecido como falha,&&
não será executado do lado direito.Aqui o pipeline esquerdo tem status de saída de sucesso, então o pipeline direito é executado e vemos sua saída.
Observe que a gramática do shell não implica ordem de execução real. Para citar uma das respostas de Gilles :
E do manual do bash:
Com base nisso,
cd ~/screenshots/ && ls screenshot* | head -n 5
ocd ~/screenshots/
seria executado primeiro,ls screenshot* | head -n 5
se o comando anterior for bem-sucedido, mashead -n 5
pode ser um primeiro processo gerado emls
vez de estar em um pipeline.Aqui é onde está especificado em
bash(1)
:Então,
&&
separa pipelines.Você poderia apenas tentar
echo hello && echo world | less
. Você verá que|
tem maior precedência (um pipeline de comandos é um comando). Lá para o seu segundo exemplo NÃO é o mesmo. No entanto, porquecd
não tem saída, você não verá diferença, de outra forma.