Mengapa tidak ada ";" setelah "lakukan" di loop sh?


28

Mengapa tidak ada ;karakter setelah dodalam loop shell ketika ditulis pada satu baris?

Inilah yang saya maksud. Ketika ditulis pada beberapa baris, sebuah forloop terlihat seperti:

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

Dan pada satu baris:

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

Semua baris yang diciutkan mendapatkan ;setelahnya kecuali untuk dobaris tersebut, dan jika Anda memasukkannya ;, itu adalah kesalahan. Seseorang mungkin jauh lebih pintar daripada saya memutuskan bahwa ini adalah hal yang tepat untuk dilakukan karena suatu alasan, tetapi saya tidak tahu apa alasannya. Sepertinya tidak konsisten dengan saya.

Sama dengan whileloop juga.

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


2
Itu konsisten: tidak ada titik koma setelah while/ forbaik.
Dmitry Grigoryev

Hal yang langsung terlintas dalam pikiran adalah bahwa itu tidak perlu. Di tempat lain, titik koma membedakan pernyataan.
Paul Draper

Karena itu akan mubazir. Tidak ada ambiguitas tanpa itu.
user207421

Jawaban:


29

Itu adalah sintaks dari perintah. Lihat Perintah Majemuk

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

Catatan khusus: do commands

Sebagian besar orang meletakkan dodan commandspada baris terpisah untuk memungkinkan keterbacaan yang lebih mudah tetapi tidak perlu, Anda dapat menulis:

for i in thing
do something
done

Saya tahu pertanyaan ini secara khusus tentang shell dan saya telah menautkan ke manual bash. Itu tidak ditulis seperti itu dalam manual shell tetapi ditulis seperti itu dalam sebuah artikel yang ditulis oleh Stephen Bourne untuk majalah byte.

Stephen berkata:

Sebuah daftar perintah adalah urutan satu atau lebih sederhana perintah dipisahkan atau dihentikan oleh baris baru atau ;(titik koma). Selain itu, kata-kata yang dicadangkan seperti lakukan dan dilakukan biasanya didahului oleh baris baru atau ;... Pada gilirannya setiap kali daftar perintah yang diikuti lakukan dijalankan.


5
Akan juga menarik mengapa tidak ada tanda titik koma for i in thing; do; something; done. Meskipun jeda baris diperbolehkan, titik koma tidak diizinkan.
rexkogitans

@gds: Ya, persis. Begitulah tata bahasa shell terstruktur juga. Ini somethingadalah subjek dan doberlaku untuk itu. Sama seperti dalam struktur kalimat bahasa Inggris.
Peter Cordes

@ gidds Sebuah literal (atau beberapa sintaks lain) diperlukan karena beberapa perintah dapat masuk dalam kondisi (dalam whilesintaks), jadi Anda memerlukan sesuatu untuk membedakan perintah kondisi dari perintah body loop. Menggunakan dountuk memisahkan mereka mungkin adalah apa yang tampaknya paling pas.
JoL

@rexkogitans Memang, saya tidak pernah menyadari bahwa itu adalah kesalahan sintaksis. Pertanyaan judul ini juga sangat cocok untuk itu. Ini mungkin ada hubungannya dengan tidak membiarkan tubuh kosong. Anda mendapatkan kesalahan sintaks yang sedikit berbeda ketika membuat tubuh kosong dengan baris baru, dan contoh Anda tidak memiliki tubuh kosong.
JoL

@rexkogitans Tanda titik koma di sini adalah bagian dari sintaks loop dan berbeda dari peran lainnya sebagai terminator daftar perintah. Baris baru berbeda karena jika tidak diperlukan atau diharapkan, itu diperlakukan seperti spasi putih biasa. Tata bahasa shell berantakan.
chepner

12

Saya tidak tahu apa alasan asli untuk sintaks ini, tetapi mari kita pertimbangkan fakta bahwa whileloop dapat mengambil banyak perintah di bagian kondisi, misalnya

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

Di sini, dokata kunci diperlukan untuk membedakan bagian kondisi dan badan utama dari loop. doadalah kata kunci seperti while, ifdan then(dan done), dan tampaknya sejalan dengan yang lain bahwa itu tidak memerlukan tanda titik koma atau baris baru setelah itu tetapi muncul segera di depan perintah.

Alternatif (digeneralisasikan ke ifdan while) akan terlihat agak jelek, kita perlu menulis mis

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

Sintaks forloop hanya mirip.


11

Sintaks shell berbasis awalan. Ini memiliki klausa yang diperkenalkan oleh kata kunci khusus. Klausul tertentu harus disatukan.

Sebuah whilelingkaran dibuat satu atau lebih pengujian perintah:

test ; test ; test ; ...

dan dengan satu atau lebih perintah tubuh:

body ; body ; body ; ...

Sesuatu harus memberi tahu shell bahwa loop sementara dimulai. Itulah tujuan dari whilekata tersebut:

while test ; test ; test ; ...

Tapi kemudian, semuanya ambigu. Perintah mana yang merupakan awal dari tubuh? Sesuatu harus menunjukkan itu, dan itulah yang dilakukan doawalan:

do body ; body ; body ; ...

dan, akhirnya, sesuatu harus menunjukkan bahwa tubuh terakhir telah terlihat; kata kunci khusus donemelakukan itu.

Kata kunci shell ini tidak memerlukan pemisahan titik koma, bahkan pada baris yang sama. Misalnya, jika Anda menutup beberapa loop bersarang, Anda bisa melakukannya done done done ....

Sebaliknya, titik koma adalah antara ... test ; body ... jika mereka berada di jalur yang sama. Titik koma itu dipahami sebagai terminator: ia termasuk dalam test. Oleh karena itu, jika dokata kunci dimasukkan di antara mereka, kata itu harus berada di antara titik koma dan body. Jika berada di sisi lain dari titik koma, itu akan salah tertanam di dalam testsintaks perintah, daripada ditempatkan di antara perintah.

Sintaksis shell awalnya dirancang oleh Stephen Bourne, dan terinspirasi oleh Algol . Bourne sangat mencintai Algol sehingga dia menggunakan banyak makro C dalam kode sumber shell untuk membuat C terlihat seperti Algol. Anda dapat menelusuri sumber shell bertanggal 1979 dari Versi 7 Unix . Makro ada di mac.h, dan mereka digunakan di semua tempat. Misalnya ifpernyataan tersebut diberikan sebagai IF... ELSE... ELIF... FI.


Kode sumber sangat mengesankan.
keithpjolley

Ya, itu adalah tur de force dari cpp.
stolenmoment

7

Saya berpendapat bahwa doini tidak terlalu istimewa di sini. Anda mendapatkan kesalahan karena ;tidak dapat memulai daftar perintah. Jika ya, perintah pertama akan kosong, dan tata bahasa tidak mengizinkan perintah kosong (penugasan variabel atau pengalihan tanpa perintah, ya, tapi sama sekali tidak ada, tidak). Ini berlaku di sejumlah tempat, di mana pun daftar perintah diharapkan:

$ 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

Bahkan:

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

Di semua tempat-tempat ini, daftar perintah diharapkan, dan demikianlah sebuah petunjuk ;tidak terduga.


Ya - cara saya menambahkan '\ n' setelah 'do' secara sewenang-wenang membuat saya berpikir bahwa 'do' adalah token yang berdiri sendiri dan bukan sesuatu yang beroperasi pada blok yang mengikuti.
keithpjolley

Bagaimana mungkin sebuah baris baru non-melarikan diri (yang jika tidak tampaknya berperilaku seperti titik koma di sintaks shell) yang diperbolehkan setelah do(dan iflain-lain), meskipun?
Henning Makholm

@ Menanyakan Sejumlah baris baru diizinkan sebelum (dan setelah) daftar perintah. Baris baru dan titik koma berperilaku berbeda dalam aspek lain juga. Misalnya, ekspansi alias dilakukan ketika seluruh baris input dibaca (yaitu, hingga baris baru berikutnya), dan bukan per perintah.
muru

3

Sintaksnya adalah:

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

Salah satu baris baru, baik dalam daftar perintah atau sebelum "lakukan" atau "selesai" dapat diganti dengan ";".

Baris baru (tetapi bukan ";") diizinkan setelah "lakukan" dan sebelum "dalam", hanya untuk membuat segalanya terlihat lebih baik, tetapi tidak masuk akal untuk meminta baris baru di sana.


3

if mirip dengan kata kuncinya: ini cukup mudah dibaca ketika perintahnya pendek:

if   test_commands
then true_commands
else false_commands
fi

Setelah saya menyadari saya melakukan arbitrer \nsetelah dosemua masuk akal (selama Anda tidak berpikir tentang tidak bisa dimasukkan ke dalam arbitrer \nantara perintah dan argumen lainnya).
keithpjolley

Nah, do, then, elsetidak args - jika mereka, Anda tidak akan diizinkan untuk menggunakan titik koma sebelum mereka. Itu adalah kata kunci shell (lihat type if then elif else fi)
glenn jackman

Saya kira saya tidak sejelas yang saya inginkan pada jawaban saya.
keithpjolley

Maksud saya adalah bahwa "lakukan" tidak seperti "perintah lain". Jadi itu mematuhi aturan yang berbeda.
glenn jackman

3

Jawaban singkat, karena ini ditentukan oleh tata bahasa shell POSIX . Apa pun di antara dodan donedianggap sebagai sekelompok pernyataan, sedangkan ;pemisah berurutan:

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

Selain itu, jika ;perlu standar akan secara eksplisit menyatakan demikian dan akan termasuk sequential_sepsetelahnya Do.


1
@ Davidbenn Maksudmu yang pertama? Itu iterasi melalui parameter posisi. Jadi jika Anda memiliki skrip bernama like ./myscript.sh abc, di mana abc adalah argumen, loop untuk i; do echo "$ i"; selesai akan loop melalui abc, meskipun saya masih berpikir itu akan membutuhkan titik koma untuk itu untuk bekerja
Sergiy Kolodyazhnyy

Ok, jadi keraguan saya dihapus juga
Sergiy Kolodyazhnyy

1

Karena do adalah kata kunci dan apa yang mengikutinya pada dasarnya adalah parameter. Penerjemah Bash tahu bahwa lebih banyak yang mengikuti. Ini mirip dengan bagaimana tidak ada ';' mengikuti i di perintah for. Anda benar-benar dapat menambahkan baris baru setelah untuk dan ketik sisanya pada baris baru dan itu akan berfungsi dengan baik.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.