Perché non c'è un “;” dopo “do” nei cicli sh?


28

Perché non c'è un ;personaggio dopo donei loop shell quando è scritto su una sola riga?

Ecco cosa intendo. Quando è scritto su più righe, un forloop assomiglia a:

$ for i in $(jot 2)
> do
>     echo $i
> done

E su una sola riga:

$ for i in $(jot 2); do echo $i; done

Tutte le linee compresse vengono contrassegnate da una ;dopo, ad eccezione della dolinea, e se si include la ;, si tratta di un errore. Qualcuno probabilmente molto più intelligente di me ha deciso che questa era la cosa giusta da fare per una ragione, ma non riesco a capire quale sia la ragione. Mi sembra incoerente.

Lo stesso whilevale per i loop.

$ while something
> do
>     anotherthing
> done
$ while something; do anotherthing; done


2
È coerente: non esiste un punto e virgola dopo while/ forneanche.
Dmitry Grigoryev il

La cosa che mi viene subito in mente è che non è proprio necessario. Altrove, i punti e virgola distinguono le dichiarazioni.
Paul Draper,

Perché sarebbe ridondante. Non c'è ambiguità senza di essa.
user207421

Risposte:


29

Questa è la sintassi del comando. Vedi Comandi composti

for name [ [in [words …] ] ; ] do commands; done

Nota in particolare: do commands

La maggior parte delle persone inserisce doe commandssu una riga separata per consentire una più facile leggibilità ma non è necessario, è possibile scrivere:

for i in thing
do something
done

So che questa domanda riguarda specificamente la shell e ho collegato il manuale di bash. Non è scritto in questo modo nel manuale della shell ma è scritto in questo modo in un articolo scritto da Stephen Bourne per la rivista byte.

Stefano dice:

Un elenco di comandi è una sequenza di uno o più comandi semplici separati o terminati da una nuova riga o ;(punto e virgola). Inoltre, le parole riservate come do e done sono normalmente precedute da una nuova riga o ;... A sua volta ogni volta che viene eseguito l'elenco dei comandi che segue do .


5
Sarebbe anche interessante il motivo per cui non ci deve essere un punto e virgola, come for i in thing; do; something; done. Sebbene sia consentita un'interruzione di riga, non è consentito un punto e virgola.
rexkogitans,

@gidds: sì, esattamente. Ecco come è strutturata anche la grammatica della shell. L' somethingargomento è e si doapplica ad esso. Proprio come nella struttura della frase inglese.
Peter Cordes,

@gidds Un letterale (o qualche altra parte della sintassi) è necessario perché più comandi possono andare nella condizione (nella whilesintassi), quindi è necessario qualcosa per differenziare i comandi della condizione dai comandi del corpo del ciclo. L'uso doper separarli è probabilmente ciò che sembrava semplicemente più appropriato.
JoL

@rexkogitans In effetti, non ho mai capito che si trattasse di un errore di sintassi. Anche questa domanda sul titolo è molto adatta. Probabilmente ha a che fare con il non consentire un corpo vuoto. Si ottiene un errore di sintassi leggermente diverso quando si crea un corpo vuoto con nuove righe e il tuo esempio non ha un corpo vuoto.
JoL

@rexkogitans Il punto e virgola qui fa parte della sintassi del ciclo e si distingue dall'altro ruolo di terminatore dell'elenco di comandi. La nuova riga è diversa perché, se non diversamente richiesto o previsto, viene trattato come uno spazio normale. La grammatica della shell è disordinata.
Chepner,

12

Non so quale sia il motivo originale di questa sintassi, ma consideriamo il fatto che i whileloop possono accettare più comandi nella sezione condizione, ad es.

while a=$(somecmd);
      [ -n "$a" ];
do
    echo "$a"
done

Qui, la doparola chiave è necessaria per distinguere la sezione condizione e il corpo principale del ciclo. doè una parola chiave come while, ife then(e done), e sembra essere in linea con le altre che non richiede un punto e virgola o una nuova riga dopo di essa ma appare immediatamente prima di un comando.

L'alternativa (generalizzata a ife while) sembrerebbe alquanto brutta, avremmo bisogno di scrivere ad es

if; [ -n "$a" ]; then
    some command here
fi

La sintassi dei forloop è semplicemente simile.


11

La sintassi della shell si basa sul prefisso. Ha clausole introdotte da parole chiave speciali. Alcune clausole devono andare insieme.

Un whileciclo è composto da uno o più comandi di test:

test ; test ; test ; ...

e con uno o più comandi del corpo:

body ; body ; body ; ...

Qualcosa deve dire alla shell che inizia un ciclo while. Questo è lo scopo della whileparola:

while test ; test ; test ; ...

Ma poi, le cose sono ambigue. Quale comando è l'inizio del corpo? Qualcosa deve indicarlo, ed è quello che fa il doprefisso:

do body ; body ; body ; ...

e, infine, qualcosa deve indicare che l'ultimo corpo è stato visto; una parola chiave speciale lo donefa.

Queste parole chiave della shell non richiedono la separazione di punti e virgola, anche sulla stessa riga. Ad esempio, se chiudi diversi loop nidificati, puoi semplicemente avere done done done ....

Piuttosto, il punto e virgola è compreso tra ... test ; body ... se sono sulla stessa riga. Quel punto e virgola è considerato un terminatore: appartiene al test. Pertanto, se doviene inserita una parola chiave tra loro, deve passare tra il punto e virgola e body. Se fosse dall'altra parte del punto e virgola, verrebbe inserito erroneamente nella testsintassi del comando, anziché collocato tra i comandi.

La sintassi della shell è stata originariamente progettata da Stephen Bourne ed è ispirata ad Algol . Bourne adorava Algol così tanto che usò molte macro C nel codice sorgente della shell per far sembrare C simile ad Algol. Puoi sfogliare le fonti shell datate 1979 dalla versione 7 Unix . Le macro sono presenti mac.he vengono utilizzate ovunque. Per esempio ifle dichiarazioni sono resi come IF... ELSE... ELIF... FI.


Il codice sorgente è impressionante.
keithpjolley

Sì, è un tour de force di cpp.
rubato il

7

Direi che donon è particolarmente speciale qui. Viene visualizzato un errore perché ;non è possibile avviare un elenco di comandi. In tal caso, il primo comando sarebbe vuoto e la grammatica non consente comandi vuoti (assegnazioni di variabili o reindirizzamenti senza un comando, sì, ma assolutamente niente, no). Ciò è vero in qualsiasi numero di luoghi, ovunque sia previsto un elenco di comandi:

$ while true; do ; echo foo; done
dash: 1: Syntax error: ";" unexpected
$ while ; true; do; echo; done
dash: 1: Syntax error: ";" unexpected
$ { ; echo foo; }
dash: 1: Syntax error: ";" unexpected
$ if ; true; then echo foo; fi
dash: 1: Syntax error: ";" unexpected

Anche:

$ ; ;
dash: 1: Syntax error: ";" unexpected

In tutti questi posti, è previsto un elenco di comandi, quindi un vantaggio ;è imprevisto.


Sì, il modo in cui ho aggiunto arbitrariamente un '\ n' dopo 'do' mi ha fatto pensare che 'do' fosse un token autonomo e non qualcosa che operasse sul blocco che segue.
keithpjolley il

Come è possibile che una nuova riga senza escape (che altrimenti sembra comportarsi come un punto e virgola nella sintassi della shell) sia consentita dopo do(e ifcosì via), però?
Henning Makholm,

@Henning È consentito qualsiasi numero di newline prima (e dopo) di un elenco di comandi. Newline e punti e virgola si comportano diversamente anche in altri aspetti. Ad esempio, l'espansione dell'alias viene eseguita quando viene letta un'intera riga di input (ovvero, fino alla nuova riga successiva) e non per comando.
muru,

3

La sintassi è:

for i in [whitespace separated list of values]
do [newline separated list of commands]
done

Una delle nuove righe, nell'elenco dei comandi o prima di "do" o "done" può essere sostituita da un ";".

Una nuova riga (ma non un ";") è consentita dopo il "do" e prima del "in", solo per rendere le cose più belle, ma non avrebbe senso richiedere una nuova riga lì.


3

if è simile con le sue parole chiave: questo è abbastanza leggibile quando i comandi sono brevi:

if   test_commands
then true_commands
else false_commands
fi

Una volta ho capito che stavo inserendo un arbitrario \ndopo doche tutto aveva un senso (a patto che tu non pensi di non essere in grado di inserire un arbitrario \ntra altri comandi e argomenti).
keithpjolley

Beh, do, then, elsenon sono args - se lo fossero, si potrebbe non essere consentito di utilizzare un punto e virgola prima di loro. Sono parole chiave della shell (vedi type if then elif else fi)
glenn jackman

Immagino di non essere stato chiaro come volevo essere sulla mia risposta.
keithpjolley

Il mio punto era che "do" non è come un "altro comando". Quindi obbedisce a regole diverse.
Glenn Jackman,

3

Risposta breve, perché specificata dalla grammatica della shell POSIX . Qualsiasi cosa tra doe doneè considerata un gruppo di istruzioni, mentre ;è un separatore sequenziale:

for_clause       : For name                                      do_group
                 | For name                       sequential_sep do_group
                 | For name linebreak in          sequential_sep do_group
                 | For name linebreak in wordlist sequential_sep do_group

do_group         : Do compound_list Done 

sequential_sep   : ';' linebreak
                 | newline_list

Inoltre, se ;fosse necessario, lo standard lo dichiarerebbe esplicitamente e includerebbe sequential_sepdopo Do.


1
@drewbenn Intendi il primo? Questo sta iterando attraverso parametri posizionali. Quindi se hai uno script chiamato come ./myscript.sh abc, dove abc sono argomenti, il ciclo per i; fare eco "$ i"; fatto sarebbe passato attraverso abc, anche se penso ancora che avrebbe bisogno di punto e virgola per funzionare
Sergiy Kolodyazhnyy

Ok, quindi anche i miei dubbi vengono chiariti
Sergiy Kolodyazhnyy il

1

Perché do è una parola chiave e ciò che segue sono essenzialmente parametri. L'interprete di Bash sa che ne segue di più. È simile a come non c'è ';' seguendo la i nel comando for. Puoi effettivamente aggiungere una nuova riga dopo l'i per e digitare il resto su una nuova riga e funzionerà bene.

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.