O cenário clássico com Precedência do 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 canal na lista, portanto 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 , por isso não tenho 100% de certeza de que essas linhas sejam equivalentes à linha anterior:
((cd ~/screenshots/ && ls screenshot*) | head -n 5)
De maneira mais geral, como conhecer o AST de uma linha de base? Em python, eu tenho uma função que me fornece a árvore, para que eu possa verificar com facilidade a ordem de operação.
|
útil saber que é apenas um conector que encaixa o stdout do LHS no stdin do RHS. Portanto, se ocd
comando no seu exemplo falhar, ele não enviará nenhuma saída parahead
, mashead
ainda assimexecute
, analisará o nada e não retornará nenhuma saída.bash
está usando um analisador yacc e não algo ad-hoc; se você passar poryacc -v
isso, forneceráy.output
uma boa gramática que mostra isso&&
e||
combina listas, e as listas acabam sendo compostas de pipelines (e não o contrário); tl; dr;A && B | C
é o mesmo queA && { B | C; }
, como esperado. Não assuma nenhuma ordem de execução entreB
eC
; os comandos em um pipeline são executados em paralelo .[...]
testes e$((...))
avaliações aritméticas; em particular,||
e&&
conforme usado com a lista de comandos na linguagem shell, tem a mesma precedência , diferentemente dos respectivos operadores naC
ou na avaliação aritmética (onde&&
se vincula mais firmemente que||
).Respostas:
Isso é equivalente a
(o grupo de comandos de cintas em conjunto sem uma subcamada ). A precedência de
|
é, portanto, mais alta (liga-se mais) que&&
e||
. Isso é,e
sempre significa que apenas saída de B é para ser dado a C . Você pode usar
(...)
ou{ ... ; }
unir comandos como uma entidade única para desambiguação, se necessário:Você pode testar isso usando alguns comandos diferentes. Se você correr
então você terá
costas:
tr a-z A-Z
maiúscula sua entrada , e você pode ver que apenasecho world
foi canalizada nela, enquantoecho hello
foi executada por conta própria.Isso é definido na gramática do shell , embora não seja muito claro: a
and_or
produção (para&&
/||
) é definida como tendo aapipeline
em seu corpo, enquantopipeline
apenas contémcommand
, o que não contémand_or
- somente 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. 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 semi-formalmente e pode ser uma missão bastante acertada. Até o próprio Bash às vezes entendeu errado , então os aspectos práticos e o ideal podem ser diferentes.
Existem analisadores externos que tentam corresponder à sintaxe e produzir uma árvore, e dentre esses eu recomendarei Morbig , que tenta ser o mais confiável.
fonte
TL; DR : separadores de lista como
;
.&
,&&
e||
decida a ordem de análise.O manual do bash nos diz:
Ou como o wiki do Bash Hacker coloca sucintamente
Portanto,
cd ~/screenshots/ && ls screenshot* | head -n 5
existe 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
. Nesse caso, usando a notação do OP, seria(A && B) | C
Vamos dar 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 o status negativo, também conhecido como falha,&&
não será executado no lado direito.Aqui o pipeline esquerdo tem status de saída bem-sucedida, portanto, o pipeline direito é executado e vemos sua saída.
Observe que a gramática do shell não implica em 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
o comandocd ~/screenshots/
seria executado primeiro,ls screenshot* | head -n 5
se o comando anterior for bem-sucedido, mashead -n 5
pode ser o primeiro processo gerado, e nãols
porque eles estão em um pipeline.fonte
cd ~/screenshots/ && ls screenshot*
ouhead -n 5
" Bem, sim, é esse o caso((cd ~/screenshots/ && ls screenshot*) | head -n 5)
. Mas não diz nada sobre o caso ambíguocd ~/screenshots/ && ls screenshot* | head -n 5
que parece ser o objetivo da pergunta (com ou sem parênteses).cd ~/screenshots/ && ls screenshot*
teria que ser processado primeiro porque as&&
listas são mais altas na ordem de precedência (com base na resposta de 10b0, pelo menos). Onde você acha que eu deveria melhorar a resposta?(cd && ls | head)
e((cd && ls) | head)
, pode ser bom ser explícito sobre qual deles você quer dizer.Aqui é onde está especificado em
bash(1)
:Então,
&&
separa tubulações.fonte
Você poderia apenas tentar
echo hello && echo world | less
. Você verá que|
tem precedência mais alta (um pipeline de comandos é um comando). Lá para o seu segundo exemplo NÃO é o mesmo. No entanto, comocd
não tem saída, você não verá nenhuma diferença.fonte