Dlaczego nie ma „;” po „z” w pętlach sh?


28

Dlaczego nie ma ;znaku dow pętli powłoki, gdy jest napisany w jednym wierszu?

Oto co mam na myśli. Gdy zapisana jest w wielu wierszach, forpętla wygląda następująco:

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

I na jednej linii:

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

Wszystkie zapadnięte linie dostać ;po nich z wyjątkiem dla dolinii, a jeśli należą ;, to jest błąd. Ktoś pewnie o wiele mądrzejszy ode mnie zdecydował, że to była właściwa rzecz z jakiegoś powodu, ale nie mogę zrozumieć, jaki jest powód. Wydaje mi się to niespójne.

To samo dotyczy whilerównież pętli.

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


2
Jest spójny: po while/ foralbo nie ma średnika .
Dmitrij Grigoriew

Rzecz, która natychmiast przychodzi mi na myśl, to to, że po prostu nie jest to konieczne. W innych miejscach średniki rozróżniają stwierdzenia.
Paul Draper,

Ponieważ byłoby zbędne. Bez niej nie ma dwuznaczności.
user207421

Odpowiedzi:


29

To jest składnia polecenia. Zobacz Polecenia złożone

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

Uwaga w szczególności: do commands

Większość ludzi umieszcza doi commandsw osobnym wierszu, aby ułatwić czytelność, ale nie jest to konieczne, możesz napisać:

for i in thing
do something
done

Wiem, że to pytanie dotyczy konkretnie powłoki i podłączyłem się do instrukcji bash. Nie jest tak napisane w podręczniku powłoki, ale jest tak napisane w artykule Stephena Bourne'a dla magazynu byte.

Stephen mówi:

Lista poleceń jest sekwencja z jednego lub więcej prostych rozkazów oddzielone lub zamkniętym końcem wiersza lub ;(średnik). Ponadto zarezerwowane słowa takie jak do i done są zwykle poprzedzane znakiem nowej linii lub ;... Z kolei za każdym razem, gdy wykonywana jest lista poleceń następująca do .


5
Byłoby również interesujące, dlaczego nie może być średnika for i in thing; do; something; done. Mimo że podział linii jest dozwolony, średnik nie jest dozwolony.
rexkogitans

@gidds: tak, dokładnie. Tak właśnie zbudowana jest gramatyka powłoki. Jest somethingto przedmiot i dodotyczy go. Tak jak w angielskiej strukturze zdań.
Peter Cordes

@gidds Dosłowny (lub jakiś inny element składni) jest konieczny, ponieważ wiele warunków może przejść w warunku (w whileskładni), więc potrzebujesz czegoś, aby odróżnić polecenia warunku od poleceń treści pętli. Używanie ich dodo rozdzielania jest prawdopodobnie najbardziej odpowiednie.
JoL

@ rexkogitans Rzeczywiście, nigdy nie zdawałem sobie sprawy, że to błąd składniowy. To pytanie tytułowe również do tego pasuje. Prawdopodobnie ma to związek z niedopuszczaniem pustego ciała. Podczas tworzenia pustego ciała z nowymi liniami pojawia się nieco inny błąd składniowy, a twój przykład nie ma pustego ciała.
JoL

@ rexkogitans Średnik tutaj jest częścią składni pętli i różni się od swojej innej roli jako terminatora listy poleceń. Nowa linia jest inna, ponieważ jeśli nie jest inaczej wymagana ani oczekiwana, jest traktowana jak zwykła biała spacja. Gramatyka powłoki jest nieporządna.
chepner

12

Nie wiem, jaki jest pierwotny powód tej składni, ale rozważmy fakt, że whilepętle mogą przyjmować wiele poleceń w sekcji warunków, np.

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

Tutaj dosłowo kluczowe jest konieczne, aby rozróżnić sekcję warunków i główny korpus pętli. doHasło jest niczym while, ifa then(i done) i wydaje się być w zgodzie z innymi, które nie wymagają średnik lub nowej linii po nim, ale pojawia się natychmiast przed poleceniem.

Alternatywa (uogólniona do ifi while) wyglądałaby trochę brzydko, musielibyśmy napisać np

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

Składnia forpętli jest po prostu podobna.


11

Składnia powłoki jest oparta na prefiksie. Ma klauzule wprowadzone przez specjalne słowa kluczowe. Niektóre klauzule muszą iść w parze.

whilePętla składa się jednego lub więcej testów polecenia:

test ; test ; test ; ...

i przez jedno lub więcej poleceń ciała:

body ; body ; body ; ...

Coś musi powiedzieć powłoce, że zaczyna się pętla while. Taki jest cel tego whilesłowa:

while test ; test ; test ; ...

Ale rzeczy są niejednoznaczne. Które polecenie jest początkiem ciała? Coś musi to wskazywać i właśnie to dorobi przedrostek:

do body ; body ; body ; ...

i wreszcie coś musi wskazywać, że widziało ostatnie ciało; donerobi to specjalne słowo kluczowe .

Te słowa kluczowe powłoki nie wymagają separacji średnikami, nawet w tym samym wierszu. Na przykład, jeśli zamkniesz kilka zagnieżdżonych pętli, możesz po prostu mieć done done done ....

Średnik jest raczej pomiędzy, ... test ; body ... jeśli są w tej samej linii. Ten średnik jest rozumiany jako terminator: należy do niego test. Dlatego, jeśli dosłowo kluczowe zostanie wstawione między nimi, musi przechodzić między średnikiem a body. Gdyby znajdował się po drugiej stronie średnika, byłby niepoprawnie osadzony testw składni polecenia, a nie umieszczony między poleceniami.

Składnia powłoki została pierwotnie zaprojektowana przez Stephena Bourne'a i jest inspirowana przez Algola . Bourne tak bardzo kochał Algol, że używał wielu makr C w kodzie źródłowym powłoki, aby C wyglądał jak Algol. Możesz przeglądać źródła powłoki datowane na 1979 rok z wersji 7 Unix . Makra są włączone mac.hi są używane wszędzie. Na przykład ifoświadczenia są renderowane jako IF... ELSE... ELIF... FI.


Kod źródłowy jest imponujący.
keithpjolley

Tak, to tour de force cpp.
stolenmoment

7

Twierdziłbym, że donie jest to tutaj szczególnie wyjątkowe. Wystąpił błąd, ponieważ ;nie można uruchomić listy poleceń. Gdyby tak było, pierwsze polecenie byłoby puste, a gramatyka nie zezwala na puste polecenia (przypisania zmiennych lub przekierowania bez polecenia, tak, ale absolutnie nic, nie). Dzieje się tak w dowolnej liczbie miejsc, wszędzie tam, gdzie oczekiwana jest lista poleceń:

$ 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

Parzysty:

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

We wszystkich tych miejscach oczekiwana jest lista poleceń, a zatem nie można oczekiwać wprowadzenia ;.


Tak - sposób, w jaki arbitralnie dodałem „\ n” po „do”, sprawił, że pomyślałem, że „do” był samodzielnym tokenem, a nie czymś, co działało na kolejnym bloku.
keithpjolley

Jak to możliwe, że niedopuszczalny znak nowej linii (który w innym przypadku wydaje się zachowywać jak średnik w składni powłoki) jest dozwolony po do(i ifitp.)?
Henning Makholm

@ Słuchanie Dowolna liczba znaków nowej linii jest dozwolona przed (i po) listą poleceń. Nowe linie i średniki zachowują się również inaczej w innych aspektach. Na przykład interpretacja aliasu jest wykonywana, gdy odczytywany jest cały wiersz danych wejściowych (tj. Do następnego nowego wiersza), a nie według polecenia.
muru

3

Składnia jest następująca:

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

Każda z nowych linii, na liście poleceń lub przed „do” lub „zrobione” może być zastąpiona przez „;”.

Nowa linia (ale nie „;”) jest dozwolona po „do” i przed „w”, tylko po to, aby wyglądać ładniej, ale nie ma sensu wymagać nowej linii.


3

if jest podobny do słów kluczowych: jest to dość czytelne, gdy polecenia są krótkie:

if   test_commands
then true_commands
else false_commands
fi

Raz zdałem sobie sprawę, było umieszczenie w dowolnym \npo donim wszystko miało sens (tak długo, jak nie myśleć o tym nie jest w stanie umieścić w dowolnym \nmiędzy innymi poleceniami i args).
keithpjolley

Dobrze, do, then, elsesą nie args - gdyby były, to nie wolno używać średnik przed nimi. Są to słowa kluczowe powłoki (patrz type if then elif else fi)
glenn jackman

Chyba nie byłem tak jasny, jak chciałem być w odpowiedzi.
keithpjolley

Chodziło mi o to, że „do” nie jest jak „inne polecenie”. Więc przestrzega różnych zasad.
glenn jackman

3

Krótka odpowiedź, ponieważ jest to określone przez gramatykę powłoki POSIX . Wszystko pomiędzy doi donejest uważane za grupę instrukcji, podczas gdy ;jest sekwencyjnym separatorem:

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

Dodatkowo, jeśli byłoby ;to konieczne, norma wyraźnie to określa i obejmuje sequential_seppóźniej Do.


1
@drewbenn Masz na myśli pierwszy? To jest iteracja poprzez parametry pozycyjne. Więc jeśli masz skrypt o nazwie jak ./myscript.sh abc, gdzie abc są argumentami, pętla dla i; wykonaj echo „$ i”; zrobione
zapętlałoby się

Ok, więc moje wątpliwości również zostały
rozwiane

1

Ponieważ do jest słowem kluczowym, a po nim są w zasadzie parametry. Tłumacz Bash wie, że więcej. Jest podobny do tego, jak nie ma ';' po i w poleceniu for. Możesz faktycznie dodać nową linię po i i wpisać resztę w nowej linii, a to będzie działać poprawnie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.