Qual è l'uso delle parentesi `()` nella definizione della funzione shell?


20

Una funzione è definita come:

do_something () {
    do it
}

Potrei capire il suo nome "do_something" e la parentesi graffa per incapsulare il codice delle azioni, ma non sono in grado di ottenere l'idea di quale sia lo scopo () qui, poiché non ci sono parametri nominati negli script Bash. Potrebbe essere meglio e semplice definirlo come

do_something {
  do it
}

Il che non è in conflitto con la sua sintassi corrente e ancora di più dichiara che non ci sono parametri nominati. A che serve ()qui?


Risposte:


44

Senza il (), la sintassi sarebbe davvero ambigua.

Ci deve essere qualche sintassi univoca per definire una funzione, e senza alterare sostanzialmente altra sintassi shell, non può essere questo:

do_something {
    # one or more commands go here
}

Hai detto che "non si confonde con la sua sintassi attuale", ma lo fa! Si noti che non si ottiene alcun tipo di errore di sintassi quando si tenta di eseguire la prima riga . Viene visualizzato un errore, ma non si tratta di un errore relativo alla sintassi. La seconda riga, con }, è un errore di sintassi, ma la prima non lo è. Invece, do_something {tenta di eseguire un comando chiamato do_somethinge passare {come argomento a quel comando:

$ do_something {
do_something: command not found

Se esiste già un comando chiamato do_something, lo stai eseguendo. Se esiste già una funzione chiamata do_something, la stai chiamando . È importante in generale che la sintassi sia inequivocabile, ma è anche importante in particolare che sia possibile ridefinire una funzione senza chiamarla accidentalmente. Definire una funzione e chiamarla non dovrebbe avere lo stesso aspetto.

Come tratta la shell {e (.

Come type {ti dirò, {è una parola chiave shell. Questo lo rende come [[. Se utilizzato in una situazione in cui sarebbe altrimenti un comando, {porta una semantica speciale. In particolare, esegue il raggruppamento dei comandi. In altre situazioni, tuttavia, può essere usato senza caratteri di escape per indicare un {carattere letterale . Ciò include la situazione del passaggio come seconda o successiva parola di un comando.

Naturalmente, Bash avrebbe potuto essere progettato per essere trattato in modo {diverso rispetto a quello attuale. Tuttavia, la sua sintassi non sarebbe più stata compatibile con la shell POSIX e Bash non sarebbe davvero una shell in stile Bourne e non sarebbe in grado di eseguire molti script di shell.

Al contrario, (è un metacarattere conchiglia. E 'sempre trattata in modo speciale se compare in un comando e non viene citato (con ' ', " "o \). Non vi è quindi alcuna ambiguità nella sintassi:

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

Ciò non potrebbe significare nient'altro. Se Bash non avesse funzioni, sarebbe un errore di sintassi, per lo stesso motivo echo foo(bar)è un errore di sintassi.

Se non ti piace davvero la ()notazione, allora puoi usare la parola chiave functione ometterla, come menziona il sudodus . Si noti che questo non fa parte della sintassi per la definizione delle funzioni nella maggior parte delle altre shell in stile Bourne - e in alcuni è supportato ma le funzioni definite in questo modo hanno una semantica diversa - e quindi uno script che lo utilizza non sarà portatile. (Il motivo per cui questa sintassi è in grado di essere inequivocabile è che functionè essa stessa una parola chiave in Bash che indica che qualunque cosa segua è l'inizio di una definizione di funzione.)

Infine, si noti che mentre la maggior parte delle definizioni delle funzioni utilizza {in pratica, è consentito qualsiasi comando composto. Se avessi una funzione di cui avresti sempre voluto eseguire il corpo in una subshell, potresti usare ( )piuttosto che { }.


1
A un livello superiore, si tratta di aspettative e leggibilità umane. Nella maggior parte dei linguaggi, in particolare in C, fortran e altri comuni nel periodo in cui sono stati sviluppati i linguaggi di scripting bash, una funzione / subroutine ha sempre un elenco di argomenti, possibilmente vuoto, racchiuso tra parentesi. Cambia quel paradigma e rendi più difficile la comprensione della lingua.
jamesqf,

15

Il ()token è dire all'interprete della shell che si sta dichiarando una funzione.

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

Un'alternativa bashèfunction

function do_something {
 echo 'do it'
}

o come one-liner puoi testare

$ bash -c "function do_something { echo 'do it'; } ; do_something"
do it

2
La functionparola chiave è un ksh-isma pre-POSIX che bash supporta per compatibilità con le versioni precedenti; comunque bash lo supporta male (non facendo in modo che le variabili funzionino come default locali, come faceva il vecchio ksh nelle funzioni dichiarate in quel modulo). Di conseguenza, non è l'ideale per il nuovo codice. Vedi wiki.bash-hackers.org/scripting/obsolete
Charles Duffy,

@CharlesDuffy, grazie per questa spiegazione :-)
sudodus

1
@CharlesDuffy Stai dicendo che ksh e bash accettano una sintassi che rende una variabile locale in ksh ma globale in bash? Non suona bene. La mia comprensione, basata in parte su quel post e controllo (in ksh93), è che ksh rende le variabili locali in meno situazioni di bash, mai più. In bash, typesetsenza -gin una funzione dichiara sempre una variabile locale. Con le functionparole chiave, le variabili dell'ambito bash e ksh allo stesso modo , no?
Eliah Kagan,

@EliahKagan Penso che ciò a cui si riferiva Charles sia stato dimostrato nella risposta di Gille qui
Sergiy Kolodyazhnyy

9

Com'è vero il tuo vero stile da tutore !

Prendi in considerazione altri stili di rinforzo perfettamente perfetti:

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

Come può la shell dire che quelle sono definizioni di funzioni e non semplicemente un comando fooseguito da elenchi di comandi? In tutti questi casi, è necessario un ulteriore fattore non ambiguo, come ()o la functionparola chiave.


A pensarci bene, in realtà non si tratta di OTBS, poiché l'unico posto in cui OTBS consente le parentesi graffe su una nuova linea è per le definizioni delle funzioni.
muru,

3
Penso che ci sia un argomento per sostenere che avevi ragione a caratterizzarlo come OTBS, perché a differenza delle funzioni in C e C ++, una definizione di funzione in uno script di shell in stile Bourne può essere nidificata direttamente nel corpo di un'altra definizione di funzione e quindi discutibilmente non merita di essere distinto. (O forse questo è lo stile popolare nella programmazione Java in cui i metodi ottengono la loro parentesi graffa di apertura sulla stessa linea, piuttosto che OTBS.) Ad ogni modo, fai un punto eccellente su come la sintassi dovrebbe imporre vincoli stretti sul posizionamento della parentesi graffa per funzionare affatto senza ()o qualcosa del genere.
Eliah Kagan,
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.