Precedenza vincolante per pipe e reindirizzamento con disgiunzioni, congiunzioni ecc.?


8

Conosco la relativa precedenza vincolante degli operatori ';', '&', '&&' o '||'

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

ma quando le pipe entrano in scena insieme a '&&' faccio fatica a capire la forza di legame e inciampare su un comando corretto o semplicemente rinunciare.

Qual è la precedenza vincolante di '|' e '>' rispetto a quanto sopra?

Esempio in cui mi confondo:

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

L'obiettivo di cui sopra è creare una miniatura se e solo se non è già presente (eseguo un cronjob quotidiano). E non mi piace l'enorme quantità di output di ffmpeg quando non c'è errore (che non è il modo Unix). Ci sono anche altre situazioni, quindi non iniziare a darmi consigli che utilizzano istruzioni separate o opzioni speciali specifiche per questi programmi. Voglio capire la precedenza vincolante.


Molto utile - lo contrassegnerei come corretto se fosse una risposta. Ancora un'altra domanda: come posso modificare la precedenza? In teoria penso {... }dovrebbe funzionare. Avrei potuto provarlo in modo non sistematico e non ottenere i risultati previsti.
Sridhar Sarnobat,

Risposte:


12

La risposta breve è che <, >e le loro varianti hanno la precedenza di legame più alta (legame più stretto), seguita da |, seguita da &&e ||, seguita da ;e &. Quindi, solo il echo "Thumbnail creation failed"viene convogliato nel file tee.

Una risposta leggermente più lunga indica che la precedenza più elevata è in realtà il raggruppamento, che può essere indicato tra parentesi o parentesi graffe. Per esempio,

A  &&  (B; C)

e

A  &&  { B; C;}

sono approssimativamente equivalenti a

if A
then
    B
    C
fi

Appunti:

  • Le parentesi ti danno una subshell; cioè, comanda Bed Cesegue in un processo figlio. Pertanto, comandi come assegnazioni di variabili o cd non avranno alcun effetto sulla shell padre. I comandi tra parentesi graffe vengono eseguiti nello stesso processo del Acomando. Pertanto, il costrutto parentesi graffa A && { B; C;}è più vicino al costrutto if- then- else.
  • Nella sintassi del controvento, ci deve essere uno spazio dopo {e e ;(o a &, o newline) prima di }.

Per ulteriori letture, vedere Quali sono gli operatori di controllo e reindirizzamento della shell? e quando è 'se' non necessario? (in particolare le mie risposte).

Per ancora più approfondimenti, vedere la (1) la pagina man di bash e POSIX specifica / definizione del Command Language Shell , in particolare Sezione 2.9, comandi della shell e la Sezione 2.10.2, Shell regole grammaticali . Questo è un tentativo di fornire un contesto per quanto sopra:

  • Cose come

    • 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

    sono tutti considerati "comandi semplici".

  • Cose come
      simple_command 1   |   simple_command 2   |   simple_command 3
    sono "condotte". La grammatica stabilisce blocchi e si basa su di essi, come è tipico per le grammatiche formali come questa (e per i linguaggi di programmazione come C), quindi un singolo "comando semplice" è considerato una "pipeline", anche se non contiene una pipa. Non ha senso (semantico) che un'assegnazione variabile sia un componente di una pipeline, ma cose come x=1 | od -abo ls -laR | z=0sono sintatticamente valide.
  • Cose come
      pipeline 1   &&   pipeline 2   ||   pipeline 3
      sono chiamati "elenchi" da bash e "elenchi AND-OR" da POSIX. Ancora una volta, una singola "pipeline" o anche un singolo "comando semplice" è considerato un "elenco AND-OR", anche se non contiene un AND o un OR.
    • Quando arrivi a cose del genere
        Elenco AND-OR 1  e   elenco AND-OR 2  ;   Elenco AND-OR 3
        la nomenclatura inizia a diventare un po 'incoerente. Bash chiama anche questi "elenchi"; POSIX li chiama "elenchi", "elenchi composti" e (raramente) "termini". Ancora una volta, un singolo "elenco AND-OR", "pipeline" o anche un singolo "comando semplice" è considerato un "elenco", anche se non contiene a &o a ;.
      • Cose come
          ( compound_list )
          e
            {  compound_list ;}
            ei comandi di controllo di flusso ( for,  if- then- else,  whileecc) sono chiamate “comandi composti”. 
          • Negli esempi sopra riportati, rende probabilmente più senso per interpretare A, Be Cdi essere condotte. Ricorda, una "pipeline" può essere un singolo "comando semplice"; non deve contenere una pipe.


            1
            Grazie per la spiegazione dettagliata sul ruolo di (.. )e {.. }. Avevo concluso da precedenti esperienze male che parentesi tonde sono stati utilizzati solo nel contesto di "comandi dinamici" (cioè $(.. )) da quando ho potuto a malapena arrivare a fare qualsiasi cosa per quanto riguarda l'associatività è stato interessato.
            Sridhar Sarnobat,
            Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
            Licensed under cc by-sa 3.0 with attribution required.