Tubos e precedência de ligação de redirecionamento com disjuntos, conjuntos, etc?

8

Conheço a relativa precedência de ligação dos operadores ';', '&', '&&' ou '||'

http://www.gnu.org/software/bash/manual/bashref.html#Lists

mas quando os tubos entram em cena, junto com '&&', luto para entender a força da amarração e tropeço em um comando correto ou simplesmente desisto.

Qual é a precedência vinculativa de '|' e '>' comparado ao acima?

Exemplo onde eu fico confuso:

ls _thumbnails/video.mp4.jpg 2>/dev/null 
    && echo "thumbnail already generated. Not regenerating" \
    && exit \
    || ffmpeg_thumbnail_create video.mp4 2>/dev/null \
    && ls _thumbnails/video.mp4.jpg \
    && echo "Thumbnail successfully created" \
    && exit \
    || echo "Thumbnail creation failed" \
    | tee ~/thumbnails.log

O objetivo do exposto acima é criar uma miniatura, se e somente se ainda não estiver presente (eu executo um cronjob diário). E eu não gosto da enorme quantidade de saída do ffmpeg quando não há erro (que não é o caminho do Unix). Também existem outras situações, portanto, não comece a me dar conselhos que usem instruções separadas ou opções especiais específicas para esses programas. Eu quero entender a precedência vinculativa.

Sridhar Sarnobat
fonte
Muito útil - eu marcaria isso correto se fosse uma resposta. Apenas mais uma pergunta - como altero a precedência? Em teoria, acho que {... }deveria funcionar. Eu poderia ter tentado sistematicamente e não obtive os resultados esperados.
Sridhar Sarnobat

Respostas:

12

A resposta é que <, >e suas variantes têm a prioridade mais alta de ligação (ligação mais apertada), seguido por |, seguido por &&e ||, seguida de ;e &. Portanto, apenas o echo "Thumbnail creation failed"é canalizado para o tee.

Uma resposta um pouco mais longa indicaria que a maior precedência é realmente o agrupamento, que pode ser indicado entre parênteses ou chaves. Por exemplo,

A  &&  (B; C)

e

A  &&  { B; C;}

são aproximadamente equivalentes a

if A
then
    B
    C
fi

Notas:

  • Os parênteses fornecem um subshell; ou seja, comandos Be Cexecutados em um processo filho. Portanto, comandos como atribuições de variáveis ​​ou cd não terão efeito no shell pai. Os comandos entre chaves são executados no mesmo processo que o Acomando. Portanto, a construção entre chaves A && { B; C;}está mais próxima da construção if- then- else.
  • Na sintaxe do colchete, deve haver um espaço após ae {uma ;(ou a &ou nova linha) antes da }.

Para uma leitura mais detalhada, consulte Quais são os operadores de controle e redirecionamento do shell? e Quando o 'se' não é necessário? (particularmente minhas respostas).

Para uma leitura ainda mais detalhada, consulte a página do manual bash (1) e a especificação / definição POSIX da Linguagem de Comando do Shell , especificamente a Seção 2.9, Comandos do Shell e a Seção 2.10.2, Regras gramaticais da Shell . Esta é uma tentativa de fornecer algum contexto para o acima exposto:

  • Coisas como

    • myVar=42
    • IFS= read a
    • date
    • cd /some/directory
    • ls -laR dir1 dir2
    • cat foo* > /tmp/allfoo
    • ls -laR dir{1,2}
    • find . -type f -name "foo*" -print > /tmp/output 2> /dev/null
    • > newfile
    • [ -f catfood ]
    • exit

    são todos considerados "comandos simples".

  • Coisas como
      simple_command 1   |   simple_command 2   |   simple_command 3
    são "gasodutos". A gramática estabelece blocos de construção e constrói sobre eles, como é típico para gramáticas formais como esta (e para linguagens de programação como C), de modo que um "comando simples" individual é considerado um "pipeline", mesmo que não contenha um cano. Não faz sentido (semântico) que uma atribuição de variável seja um componente de um pipeline, mas coisas como x=1 | od -abou ls -laR | z=0são sintaticamente válidas.
  • Coisas como
      gasoduto 1 e   &   gasoduto 2   ||   gasoduto 3
      são chamados de "listas" pelo bash e "AND-OR lists" pelo POSIX. Novamente, um "pipeline" individual ou mesmo um "comando simples" individual é considerado uma "lista AND-OR", mesmo que não contenha um AND ou um OR.
    • Quando você chega a coisas como
        Lista AND-OR 1  e   lista AND-OR 2  ;   Lista AND-OR 3
        a nomenclatura começa a ficar um pouco inconsistente. Bash chama essas "listas" também; O POSIX os chama de "listas", "listas compostas" e (raramente) "termos". Novamente, uma "lista E-OU" individual, "pipeline" ou mesmo um "comando simples" individual é considerado uma "lista", mesmo que não contenha a &ou a ;.
      • Coisas como
          ( lista_composta )
          e
            {  lista_composta ;}
            e os comandos de controlo de fluxo ( for,  if- then- else,  while, etc.) são chamados “compostos” comandos. 
          • Nos exemplos acima, provavelmente faz mais sentido para interpretar A, Be Cpara ser condutas. Lembre-se, um "pipeline" pode ser um "comando simples" individual; não precisa conter um cano.

            G-Man diz que 'restabelece Monica'
            fonte
            1
            Obrigado pela explicação detalhada sobre o papel do (.. )e {.. }. Concluí, por experiências ruins anteriores, que colchetes eram usados ​​apenas no contexto de "comandos dinâmicos" (isto é $(... )), pois eu mal conseguia que eles fizessem qualquer coisa no que se refere à associatividade.
            Sridhar Sarnobat