Quando utilizzare () vs. {} in bash?


74

Sto studiando script di shell con bash e ho bisogno di sapere la differenza tra (...)e {...}. Come si fa a scegliere tra i due quando si scrive una sceneggiatura?



3
Intendevi solo nel contesto del raggruppamento dei comandi?
heemayl

Risposte:


87

Se si desidera che gli effetti collaterali dell'elenco dei comandi influiscano sulla shell corrente , utilizzare {...}
Se si desidera eliminare eventuali effetti collaterali, utilizzare(...)

Ad esempio, potrei usare una subshell se:

  • voglio cambiare $IFSper alcuni comandi, ma non voglio cambiare a $IFSlivello globale per la shell corrente
  • cdda qualche parte, ma non voglio cambiare il $PWDper la shell corrente

Vale la pena notare che le parentesi possono essere utilizzate nella definizione di una funzione:

  • uso normale: parentesi graffe: il corpo della funzione viene eseguito nella shell corrente; gli effetti collaterali rimangono dopo il completamento della funzione

    $ count_tmp() { cd /tmp; files=(*); echo "${#files[@]}"; }
    $ pwd; count_tmp; pwd
    /home/jackman
    11
    /tmp
    $ echo "${#files[@]}"
    11    
    
  • uso insolito: parentesi: il corpo della funzione viene eseguito in una subshell; gli effetti collaterali scompaiono quando esce la subshell

    $ cd ; unset files
    $ count_tmp() (cd /tmp; files=(*); echo "${#files[@]}")
    $ pwd; count_tmp; pwd
    /home/jackman
    11
    /home/jackman
    $ echo "${#files[@]}"
    0
    

Documentazione


11
Dopo molti anni di sviluppo della shell non sapevo che potevi usare le parentesi per eseguire le funzioni nei subshells. Che grande idea evitare di inquinare lo spazio dei nomi globale!
l0b0

7
L'uso della localparola chiave aiuta a ripulire l'inquinamento.
Glenn Jackman,

2
Sì, ma devi ricordarti di dichiarare ogni variabile locale e ingombra il codice.
l0b0

4
Suggerimento: se desideri funzioni prive di effetti collaterali ma evita l'insolita sintassi della dichiarazione di funzione (di cui gli editor di codici potrebbero non essere a conoscenza), usa semplicemente le parentesi sulla chiamata di funzione anziché la dichiarazione:pwd; (count_tmp); pwd;
Juve

2
alla shell ... foo () (:;) equivale a foo () {(:;); } Ecco come lo segnala se lo chiedi!
Anthony

23

Dalla documentazione ufficiale di bash :

()

( list )

Posizionando un elenco di comandi tra parentesi si crea un ambiente subshell e ciascuno dei comandi nell'elenco viene eseguito in quella subshell. Poiché l'elenco viene eseguito in una subshell, le assegnazioni delle variabili non rimangono attive dopo il completamento della subshell.

{}

{ list; }

Posizionare un elenco di comandi tra parentesi graffe fa sì che l'elenco venga eseguito nel contesto della shell corrente. Non viene creata alcuna subshell. È richiesto il seguente punto e virgola (o newline).


9

Il codice in "{}" viene eseguito nel thread / processo / ambiente corrente e le modifiche vengono conservate, per dirla in modo più sintetico, il codice viene eseguito nell'ambito corrente.
Il codice in '()' viene eseguito all'interno di un processo figlio separato di bash che viene scartato dopo l'esecuzione. Questo processo figlio viene spesso definito come una sotto-shell e può essere pensato come un nuovo ambito simile a un bambino.

Ad esempio, considerare quanto segue ...

 ~ # { test_var=test }
 ~ # echo $test_var
 test
 ~ # ( test_var2=test2 )
 ~ # echo $test_var2

 ~ # 

Si noti nel primo esempio con '{}' la variabile è ancora impostata anche dopo la chiusura '}', mentre nell'esempio con '()' la variabile non è impostata al di fuori dell'ambito di '()'.


4

(...)sono usati per eseguire il codice in una sotto-shell. Il codice usato tra {...}non verrà usato in una sotto-shell.

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.