Tubo B a D? - A && B || C D

14

Existe uma maneira de reescrever a estrutura de comando A && B || C | Dpara que B ou C seja canalizado para D?

Com o comando atual, apenas B ou C e D são executados.

Por exemplo:

insira a descrição da imagem aqui

Philip Kirkbride
fonte

Respostas:

31

Sim, no bash você pode usar parênteses:

(A && B || C) | D

Dessa forma, a saída de A && B || Cserá canalizada D.

sombrio
fonte
6
Ou A && (B || C) | D, se você não quer B, C ou D para ser executado quando uma falha
Zwol
2
Você também pode usar parênteses em sh:)
EKons
Não está claro se o uso de parênteses em vez de chaves foi uma decisão intencional (se sim, quais são os benefícios?) Ou apenas uma supervisão. Você poderia explicar?
precisa
O @TomFenech Parens faz com que a expressão seja executada em um sub-shell, que é um processo (único) separado do POV do restante do script. Portanto, qualquer que seja a saída da expressão dentro de parens, ela será canalizada. (Uma vez que Aestá dentro da subcamada, este também inclui A.)
jpaugh
1
@jpaugh Eu acho que o seu ponto de vista sobre isolamento é bom, mas a saída seria canalizada da mesma maneira ao usar chaves, não?
21417 Tom Tomech
14

Você pode escrever isso como

if A; then B; else C; fi | D

Você diz que deseja executar um Bou outro C, mas A && B || Cnão consegue isso. Se Afor bem-sucedido, mas for Bexecutado e falhar, ele será executado C.

Nota 1: se você, de alguma forma, pode garantir que Bsempre seja bem-sucedido e queira continuar com uma versão curta, eu ainda optaria por

{ A && B || C; } | D

acabou ( ... ), pois o último força desnecessariamente a criação de um novo subshell, que pode ou não ser otimizado.

Nota 2: ambas as formas assumem que Anão produz saída, o que é verdade no seu exemplo, mas não necessariamente no geral. Isso pode ser evitado por

A; if [ "$?" -eq 0 ]; then B; else C; fi | D
hvd
fonte
Tem certeza de que { … }não força a criação de um subshell devido ao pipe? I observar o seguinte comportamento: pgrep bashe pgrep bash | cate if true; then pgrep bash; fie { pgrep bash; }têm uma linha de saída; ( pgrep bash; )e ( pgrep bash; ) | cate { pgrep bash; } | cate if true; then pgrep bash; fi | cattem duas linhas de saída.
precisa saber é
@wchargin ... | ...faz com que um subshell seja criado, isso é inevitável. ( ... ), pelo menos em teoria, faz com que seja criado um subshell adicional que { ...; }evite, mas é isso que eu quis dizer com "pode ​​ou não ser otimizado": é possível que, nesse caso em particular, o shell perceba que não importa, o efeito seria o mesmo.
HVD
5

A resposta do aceitador está correta, mas não cobre o caso de uso potencial para não ter a saída Acomo entrada de D. Para conseguir isso, você precisará de um redirecionamento de fluxo, Adependendo de suas necessidades.

  • Se você deseja descartar a saída de Aqualquer maneira:

    { A >/dev/null && B || C; } | D
  • Se você deseja ver a saída do Aterminal:

    { A >/dev/tty && B || C; } | D
  • Se você precisar da saída Acomo entrada de um comando subsequente, Eprecisará de um grupo de comandos adicional e redirecionamento de fluxo:

    { { A >&3 && B || C; } | D; } 3>&1 | E

Se tudo isso parecer muito misterioso para você (como para mim), recomendo que você use a variável especial do shell para o status de saída Ae trabalhe com isso:

A
if [ $? -eq 0 ]; then
  B
else
  C
fi |
D

Se você quer ser mais conciso, mas não muito misterioso, sugiro o seguinte:

A; { [ $? -eq 0 ] && B || C; } | D

(Veja também a última parte da resposta do hvd que eu não notei quando escrevi minha resposta original.)

David Foerster
fonte
Minha resposta cobre isso. Veja o que coloquei na minha "Nota 2:", onde simplesmente Asaí do pipeline.
Hv
@ DVD: Você está correto e obrigado por apontar essa parte da sua resposta para mim! Alterei minha reivindicação e dei crédito a você de acordo.
David Foerster