Apa gunanya tanda kurung `()` dalam definisi fungsi shell?

20

Fungsi didefinisikan sebagai:

do_something () {
    do it
}

Saya bisa memahami namanya 'do_something' dan kurung kurawal untuk merangkum kode tindakan, tetapi saya tidak bisa mendapatkan ide apa tujuan di ()sini, karena tidak ada parameter bernama dalam skrip Bash. Mungkin lebih baik dan mudah untuk mendefinisikannya sebagai

do_something {
  do it
}

Yang tidak bertentangan dengan sintaksis saat ini, dan bahkan lebih menyatakan bahwa tidak ada parameter bernama. Apa gunanya di ()sini?

Aljabar
sumber
1
terkait: unix.stackexchange.com/questions/73750/…
Carlos Campderrós

Jawaban:

44

Tanpa itu (), sintaks benar-benar akan ambigu.

Harus ada beberapa sintaks yang tidak ambigu untuk mendefinisikan suatu fungsi, dan tanpa secara substansial mengubah sintaksis shell lainnya , tidak mungkin ini:

do_something {
    # one or more commands go here
}

Anda mengatakan ini "tidak bertentangan dengan sintaksis saat ini," tetapi itu terjadi! Perhatikan bahwa Anda tidak mendapatkan kesalahan sintaksis apa pun ketika Anda mencoba menjalankan baris pertama itu . Anda mendapatkan kesalahan, tetapi itu bukan kesalahan tentang sintaksis. Baris kedua, dengan }, adalah kesalahan sintaksis, tetapi baris pertama tidak. Sebagai gantinya, do_something {upaya untuk menjalankan perintah yang dipanggil do_somethingdan lulus {sebagai argumen untuk perintah itu:

$ do_something {
do_something: command not found

Jika sudah ada perintah yang dipanggil do_something, Anda menjalankannya. Jika sudah ada fungsi yang dipanggil do_something, Anda memanggilnya . Sangat penting secara umum bahwa sintaks tidak ambigu, tetapi juga penting secara khusus bahwa mungkin untuk mendefinisikan kembali suatu fungsi tanpa secara tidak sengaja memanggilnya. Mendefinisikan suatu fungsi dan memanggilnya seharusnya tidak terlihat sama.

Bagaimana shell memperlakukan {dan (.

Seperti yang type {akan memberitahu Anda, {adalah kata kunci shell. Ini membuatnya seperti [[. Jika digunakan dalam situasi di mana itu akan menjadi perintah, {membawa semantik khusus. Secara khusus, ia melakukan pengelompokan perintah. Dalam situasi lain, bagaimanapun, ini dapat digunakan tanpa tanda untuk menunjukkan {karakter literal . Ini termasuk situasi melewatinya sebagai kata kedua atau selanjutnya dari suatu perintah.

Tentu saja, Bash bisa dirancang untuk memperlakukan secara {berbeda dari yang ada saat ini. Namun, sintaksnya tidak lagi kompatibel dengan shell POSIX, dan Bash tidak akan benar-benar menjadi shell bergaya Bourne dan tidak akan mampu menjalankan banyak skrip shell.

Sebaliknya, (adalah metacharacter shell. Itu selalu diperlakukan khusus jika muncul dalam sebuah perintah dan tidak dikutip (dengan ' ', " "atau \). Dengan demikian tidak ada ambiguitas dalam sintaks:

do_something() {
    # one or more commands go here
}

Itu tidak bisa berarti apa-apa lagi. Jika Bash tidak memiliki fungsi, maka itu akan menjadi kesalahan sintaksis, untuk alasan yang sama echo foo(bar)adalah kesalahan sintaksis.

Jika Anda benar-benar tidak menyukai ()notasi maka Anda dapat menggunakan kata kunci functiondan menghilangkannya, seperti yang disebutkan dalam sudodus . Perhatikan bahwa ini bukan bagian dari sintaks untuk mendefinisikan fungsi di sebagian besar shell gaya Bourne lainnya - dan dalam beberapa, ini didukung tetapi fungsi yang didefinisikan memiliki semantik berbeda - dan jadi skrip yang menggunakannya tidak akan portabel. (Alasan sintaks ini bisa tidak ambigu adalah karena functionitu sendiri kata kunci dalam Bash yang menandakan bahwa apa pun yang mengikutinya adalah awal dari definisi fungsi.)

Akhirnya, perhatikan bahwa sementara sebagian besar definisi fungsi digunakan {dalam praktiknya, perintah majemuk apa pun diizinkan. Jika Anda memiliki fungsi yang tubuhnya ingin selalu Anda jalankan dalam subkulit, Anda bisa menggunakan ( )alih-alih { }.

Eliah Kagan
sumber
1
Pada tingkat yang lebih tinggi, ini tentang harapan dan keterbacaan manusia. Di sebagian besar bahasa, terutama dalam bahasa C, fortran, dan lainnya yang umum sekitar waktu bahasa scripting bash dikembangkan, fungsi / subrutin selalu memiliki daftar argumen, mungkin kosong, yang tertutup dalam tanda kurung. Ubah paradigma itu, dan Anda membuatnya lebih sulit untuk memahami bahasa.
jamesqf
15

The ()token untuk memberitahu shell interpreter bahwa Anda menyatakan fungsi.

$ do_something () { echo 'do it'; } ; do_something
do it

Alternatif dalam bashadalahfunction

function do_something {
 echo 'do it'
}

atau sebagai one-liner, Anda dapat menguji

$ bash -c "function do_something { echo 'do it'; } ; do_something"
do it
sudodus
sumber
2
Kata functionkunci adalah ksh-isme pra-POSIX yang didukung bash untuk kompatibilitas mundur; Namun, bash mendukungnya dengan buruk (tidak menjadikan variabel sebagai default-lokal fungsi, seperti yang dilakukan ksh lama pada fungsi yang dideklarasikan dalam formulir itu). Akibatnya, itu tidak ideal untuk kode baru. Lihat wiki.bash-hackers.org/scripting/obsolete
Charles Duffy
@CharlesDuffy, Terima kasih atas penjelasan ini :-)
sudodus
1
@CharlesDuffy Apakah Anda mengatakan ksh dan bash menerima beberapa sintaks yang membuat variabel lokal di ksh tetapi global dalam bash? Kedengarannya tidak benar. Pemahaman saya, sebagian didasarkan pada posting itu dan memeriksa (dalam ksh93), adalah bahwa ksh membuat variabel lokal dalam situasi lebih sedikit daripada bash, tidak pernah lebih. Dalam bash, typesettanpa -gfungsi selalu mendeklarasikan variabel lokal. Dengan functionvariabel lingkup kata kunci, bash, dan ksh dengan cara yang sama , bukan?
Eliah Kagan
@EliahKagan Saya pikir apa yang merujuk Charles telah ditunjukkan dalam jawaban Gille di sini
Sergiy Kolodyazhnyy
9

Betapa sangat benar-benar-kuat-gaya- diri Anda!

Pertimbangkan gaya penahan lain yang sangat bagus:

foo
{
  ...
}
foo
  {
    ...
  }
foo
  while ...; do ...; done # Look, ma! No braces!
foo
( ... ) # Look, ma! No braces and a subshell to boot!

Bagaimana shell dapat mengatakan bahwa itu adalah definisi fungsi dan bukan hanya perintah yang foodiikuti oleh daftar perintah? Dalam semua kasus ini, faktor disambiguasi tambahan, seperti ()atau functionkata kunci diperlukan.

muru
sumber
Setelah dipikir-pikir, itu sebenarnya bukan OTBS, karena satu-satunya tempat OTBS memungkinkan kawat gigi pada baris baru adalah untuk definisi fungsi.
muru
3
Saya pikir ada argumen yang harus dibuat bahwa Anda benar untuk mencirikannya sebagai OTBS, karena tidak seperti fungsi dalam C dan C ++, definisi fungsi dalam skrip shell Bourne-style dapat bersarang secara langsung di tubuh definisi fungsi lain dan dengan demikian bisa dibilang tidak pantas dibedakan. (Atau mungkin ini adalah gaya yang populer di pemrograman Java di mana metode mendapatkan brace pembuka pada baris yang sama, daripada OTBS.) Bagaimanapun, Anda membuat titik yang sangat baik tentang bagaimana sintaks harus memberlakukan batasan ketat pada penempatan brace untuk bekerja sama sekali tanpa ()atau sesuatu seperti itu.
Eliah Kagan