¿Tuberías y precedencia de enlace de redireccionamiento con disjuntos, conjunciones, etc.

8

Sé de la precedencia de enlace relativa de los operadores ';', '&', '&&' o '||'

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

pero cuando las tuberías entran en escena junto con '&&', lucho por comprender la fuerza de unión y tropezar con un comando correcto o simplemente renunciar.

¿Cuál es la precedencia vinculante de '|' y '>' en comparación con lo anterior?

Ejemplo donde me confundo:

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

El objetivo de lo anterior es crear una miniatura si y solo si no está presente (ejecuto un cronjob diario). Y no me gusta la gran cantidad de salida de ffmpeg cuando no hay error (que no es la forma de Unix). También hay otras situaciones, así que no empieces a darme consejos que usen declaraciones separadas u opciones especiales específicas para estos programas. Quiero entender la precedencia vinculante.

Sridhar Sarnobat
fuente
Muy útil: marcaría esto correctamente si fuera una respuesta. Solo una pregunta más: ¿cómo modifico la precedencia? En teoría creo que {... }debería funcionar. Podría haberlo intentado de forma no sistemática y no haber obtenido los resultados esperados.
Sridhar Sarnobat

Respuestas:

12

La respuesta corta es que <, >y sus variantes tienen la mayor precedencia de enlace (enlace más ajustado), seguidas de |, seguidas de &&y ||, seguidas de ;y &. Por lo tanto, solo echo "Thumbnail creation failed"se canaliza al tee.

Una respuesta un poco más larga indicaría que la prioridad más alta es en realidad la agrupación, que se puede indicar con paréntesis o llaves. Por ejemplo,

A  &&  (B; C)

y

A  &&  { B; C;}

son aproximadamente equivalentes a

if A
then
    B
    C
fi

Notas:

  • Los paréntesis te dan una subshell; es decir, manda By Cejecuta en un proceso hijo. Por lo tanto, los comandos como asignaciones variables o cd no tendrán efecto en el shell principal. Los comandos entre llaves se ejecutan en el mismo proceso que el Acomando. Por lo tanto, el constructo de abrazadera A && { B; C;}está más cerca del if- then- elseconstructo.
  • En la sintaxis de llaves, debe haber un espacio después de {y a ;(o a &, o nueva línea) antes de }.

Para leer más, vea ¿Cuáles son los operadores de control y redireccionamiento del shell?y ¿ Cuándo es 'si' no es necesario? (particularmente mis respuestas).

Para obtener más información, consulte la página de manual bash (1) y la especificación / definición POSIX del lenguaje de comandos de Shell , específicamente la Sección 2.9, Comandos de Shell y la Sección 2.10.2, Reglas de gramática de Shell . Este es un intento de proporcionar algo de contexto para lo anterior:

  • Cosas 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

    todos se consideran "comandos simples".

  • Cosas como
      simple_command 1   |   simple_command 2   |   simple_command 3
    son "tuberías". La gramática establece bloques de construcción y se basa en ellos, como es típico para gramáticas formales como esta (y para lenguajes de programación como C), por lo que un "comando simple" individual se considera una "tubería", a pesar de que no contiene un tubo. No tiene sentido (semántico) que una asignación variable sea un componente de una tubería, pero cosas como x=1 | od -abols -laR | z=0 son sintácticamente válidas.
  • Cosas como
      tubería 1   &&   tubería 2   ||   tubería 3
      son llamados "listas" por bash y "listas AND-OR" por POSIX. Una vez más, una "tubería" individual o incluso un "comando simple" individual se considera una "lista AND-OR", aunque no contenga un AND o un OR.
    • Cuando llegas a cosas como
        Y-O lista 1  &   AND-OR lista 2  ;   Y-O lista 3
        La nomenclatura comienza a ser un poco inconsistente. Bash llama a estas "listas" también; POSIX los llama "listas", "listas compuestas" y (raramente) "términos". Una vez más, una "lista AND-OR" individual, una "tubería" o incluso un "comando simple" individual se considera una "lista", aunque no contenga a &o a ;.
      • Cosas como
          ( lista_compuesta )
          y
            {  lista_compuesta ;}
            y los comandos de control de flujo ( for,  if-then - else,  while, etc.) se denominan “comandos compuestos”. 
          • En los ejemplos anteriores, es probable que tenga más sentido para interpretar A, By Cque sea tuberías. Recuerde, una "tubería" puede ser un "comando simple" individual; No necesita contener una tubería.

            G-Man dice 'restablecer a Mónica'
            fuente
            1
            Gracias por la explicación detallada sobre el papel de (.. )y {.. }. De la mala experiencia anterior había concluido que los corchetes solo se usaban en el contexto de los "comandos dinámicos" (es decir, $(.. )) ya que apenas podía lograr que hicieran algo en lo que respecta a la asociatividad.
            Sridhar Sarnobat