Jaki jest pożytek z nawiasów `()` w definicji funkcji powłoki?

20

Funkcja jest zdefiniowana jako:

do_something () {
    do it
}

Mogłem zrozumieć jego nazwę „do_something” i nawias klamrowy do enkapsulacji kodu akcji, ale nie jestem w stanie zrozumieć, jaki jest cel tego ()tutaj, ponieważ w skryptach Bash nie ma nazwanych parametrów. Zdefiniowanie go jako może być łatwiejsze i prostsze

do_something {
  do it
}

Co nie jest sprzeczne z jego bieżącą składnią, a jeszcze bardziej deklaruje, że nie ma nazwanych parametrów. Jaki jest pożytek z ()tego miejsca?

Algebra
źródło
1
powiązane: unix.stackexchange.com/questions/73750/…
Carlos Campderrós

Odpowiedzi:

44

Bez tego ()składnia byłaby naprawdę niejednoznaczna.

Musi istnieć pewna jednoznaczna składnia do zdefiniowania funkcji i bez zasadniczej zmiany innej składni powłoki, nie może być tak:

do_something {
    # one or more commands go here
}

Powiedziałeś, że to „nie ma nic wspólnego z obecną składnią”, ale tak się dzieje! Zauważ, że podczas próby uruchomienia pierwszego wiersza tego nie pojawia się żaden błąd składniowy . Wystąpił błąd, ale nie jest to błąd dotyczący składni. Drugi wiersz, z }, jest błędem składniowym, ale pierwszy wiersz nie. Zamiast tego do_something {próbuje uruchomić wywołaną komendę do_somethingi przekazać {jako argument do tej komendy:

$ do_something {
do_something: command not found

Jeśli istnieje już wywołane polecenie do_something, uruchamiasz je. Jeśli istnieje już wywołana funkcja do_something, wywołujesz ją . Ogólnie ważne jest, aby składnia była jednoznaczna, ale ważne jest również, aby możliwe było ponowne zdefiniowanie funkcji bez przypadkowego jej wywołania. Zdefiniowanie i wywołanie funkcji nie powinno wyglądać tak samo.

Jak leczy się skorupa {i (.

Jak type {powiedzą, {jest słowem kluczowym powłoki. To sprawia, że ​​tak [[. Jeśli zostanie użyty w sytuacji, w której inaczej byłby poleceniem, {niesie specjalną semantykę. W szczególności wykonuje grupowanie poleceń. W innych sytuacjach można go jednak użyć bez ukrycia, aby {opisać dosłowny charakter. Obejmuje to sytuację przekazania go jako drugie lub kolejne słowo polecenia.

Oczywiście Bash mógł zostać zaprojektowany tak, aby traktować go {inaczej niż obecnie. Jednak jego składnia nie byłaby wówczas kompatybilna z powłoką POSIX, a Bash tak naprawdę nie byłby powłoką w stylu Bourne'a i nie byłby w stanie uruchomić wielu skryptów powłoki.

Natomiast (metaznak powłoki. To jest zawsze traktowane szczególnie jeśli okaże się w poleceniu i nie jest podawana (z ' ', " "lub \). Nie ma zatem dwuznaczności w składni:

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

To nie mogło znaczyć nic innego. Gdyby Bash nie miał funkcji, byłby to błąd składniowy, z tego samego powodu echo foo(bar)to błąd składniowy.

Jeśli naprawdę nie podoba ci się ()notacja, możesz użyć słowa kluczowego functioni pominąć je, jak wspomina sudodus . Zauważ, że nie jest to część składni definiowania funkcji w większości innych powłok w stylu Bourne'a - i w niektórych jest obsługiwana, ale funkcje zdefiniowane w ten sposób mają inną semantykę - i dlatego skrypt, który jej używa, nie będzie przenośny. (Powodem, dla którego ta składnia może być jednoznaczna, jest to, że functionsamo w Bash jest słowem kluczowym, które oznacza, że ​​cokolwiek następuje po nim, jest początkiem definicji funkcji.)

Na koniec zauważ, że chociaż większość definicji funkcji używa {w praktyce, każde polecenie złożone jest dozwolone. Jeśli miałeś funkcję, której ciało zawsze chciałeś uruchamiać w podpowłoce, możesz użyć ( )zamiast niej { }.

Eliah Kagan
źródło
1
Na wyższym poziomie chodzi o ludzkie oczekiwania i czytelność. W większości języków, szczególnie w C, fortran i innych powszechnych w czasie, gdy opracowywano języki skryptowe bash, funkcja / podprogram zawsze ma listę argumentów, być może pustą, zamkniętą w nawiasach. Zmień ten paradygmat, a utrudnisz zrozumienie języka.
jamesqf
15

()Token jest powiedzieć interpreter powłoki, które są deklarowania funkcji.

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

Alternatywą bashjestfunction

function do_something {
 echo 'do it'
}

lub jako jedno-liniowy możesz przetestować

$ bash -c "function do_something { echo 'do it'; } ; do_something"
do it
sudodus
źródło
2
Słowo functionkluczowe to ksh-ism sprzed POSIX, który bash obsługuje dla kompatybilności wstecznej; jednak bash źle to obsługuje (nie czyni zmiennych zmiennymi lokalnymi domyślnymi, jak stary ksh w funkcjach zadeklarowanych w tej formie). W związku z tym nie jest idealny dla nowego kodu. Zobacz wiki.bash-hackers.org/scripting/obsolete
Charles Duffy
@CharlesDuffy, Dzięki za wyjaśnienie :-)
sudodus
1
@CharlesDuffy Czy mówisz, że ksh i bash akceptują składnię, która sprawia, że ​​zmienna jest lokalna w ksh, ale globalna w bash? To nie brzmi dobrze. Rozumiem, częściowo oparty na tym poście i sprawdzaniu (w ksh93), że ksh sprawia, że ​​zmienne są lokalne w mniejszej liczbie sytuacji niż bash, nigdy więcej. W bash, typesetbez -gw funkcji zawsze deklaruje zmienną lokalną. Czy functionzmienne zasięgu słowa kluczowego, bash i ksh są takie same , czyż nie?
Eliah Kagan
@EliahKagan Myślę, że to, o czym mówił Charles, zostało wykazane w odpowiedzi
Gille'a
9

Jak bardzo jeden z was ma styl prawdziwej klamry !

Zastanów się nad innymi, doskonale dobranymi stylami usztywnień:

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

W jaki sposób powłoka może stwierdzić, że są to definicje funkcji, a nie tylko polecenie foopoprzedzone listami poleceń? We wszystkich tych przypadkach konieczny jest dodatkowy czynnik jednoznaczny, taki jak ()lub functionsłowo kluczowe.

muru
źródło
Z drugiej strony, tak naprawdę nie jest to OTBS, ponieważ jedynym miejscem, w którym OTBS zezwala na nawiasy klamrowe w nowej linii, są definicje funkcji.
mur
3
Myślę, że należy argumentować, że miałeś rację charakteryzując go jako OTBS, ponieważ w przeciwieństwie do funkcji w C i C ++, definicja funkcji w skrypcie powłoki w stylu Bourne'a może być zagnieżdżona bezpośrednio w treści innej definicji funkcji, a więc prawdopodobnie nie zasługuje na wyróżnienie. (A może jest to styl popularny w programowaniu Java, w którym metody otwierają nawiasy otwierające na tej samej linii, a nie OTBS.) Tak czy inaczej, doskonale rozumiesz, w jaki sposób składnia musiałaby nałożyć ścisłe ograniczenia na umieszczanie nawiasów w celu działania w ogóle bez ()lub coś w tym rodzaju.
Eliah Kagan