Quand utiliser () contre {} dans bash?

74

J'étudie les scripts shell avec bash et j'ai besoin de connaître la différence entre (...)et {...}. Comment peut-on choisir entre les deux lors de l'écriture d'un script?

Fat Mind
la source
1
Voir wiki.bash-hackers.org
Helio
3
Vous vouliez dire dans le contexte du groupe de commandes seulement?
Heemayl

Réponses:

87

Si vous souhaitez que les effets secondaires de la liste de commandes affectent votre shell actuel , utilisez {...}
Si vous souhaitez supprimer tout effet secondaire, utilisez(...)

Par exemple, je pourrais utiliser un sous-shell si je:

  • veux modifier $IFSquelques commandes, mais je ne veux pas modifier $IFSglobalement le shell actuel
  • cdquelque part, mais je ne veux pas changer le $PWDpour le shell actuel

Il est intéressant de noter que les parenthèses peuvent être utilisées dans une définition de fonction:

  • utilisation normale: accolades: le corps de la fonction s'exécute dans le shell actuel; les effets secondaires persistent une fois la fonction terminée

    $ count_tmp() { cd /tmp; files=(*); echo "${#files[@]}"; }
    $ pwd; count_tmp; pwd
    /home/jackman
    11
    /tmp
    $ echo "${#files[@]}"
    11    
    
  • utilisation inhabituelle: parenthèses: le corps de la fonction s'exécute dans un sous-shell; les effets secondaires disparaissent à la sortie du sous-shell

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

Documentation

Glenn Jackman
la source
11
Après de nombreuses années de développement de shell, je ne savais pas que vous pouviez utiliser des parenthèses pour exécuter des fonctions dans des sous-shell. Quelle bonne idée d'éviter de polluer l'espace de noms mondial!
l0b0
7
L'utilisation du localmot clé contribue largement à réduire cette pollution.
Glenn Jackman
2
Oui, mais vous devez vous rappeler de déclarer chaque variable locale, et cela encombrerait le code.
l0h08 le
4
Astuce: Si vous voulez des fonctions sans effets secondaires tout en évitant la syntaxe inhabituelle de déclaration de fonctions (que les éditeurs de code ne connaissent peut-être pas), utilisez simplement des parenthèses sur l'appel de fonction plutôt que la déclaration:pwd; (count_tmp); pwd;
Juve
2
à la coquille ... foo () (:;) est équivalent à foo () {(:;); } C'est comme ça que ça se passe si vous le demandez!
anthony
23

De la documentation officielle de bash :

()

( list )

Le fait de placer une liste de commandes entre parenthèses entraîne la création d’un environnement de sous-shell et l’exécution de chacune des commandes de la liste dans ce sous-shell. Comme la liste est exécutée dans un sous-shell, les affectations de variables ne restent pas en vigueur une fois le sous-shell terminé.

{}

{ list; }

Si vous placez une liste de commandes entre des accolades, la liste sera exécutée dans le contexte actuel du shell. Aucun sous-shell n'est créé. La liste suivante point-virgule (ou nouvelle ligne) est requise.

Trauma numérique
la source
9

Le code dans '{}' est exécuté dans le thread / processus / environnement actuel et les modifications sont conservées. Pour le dire plus succinctement, le code est exécuté dans la portée actuelle.
Le code dans '()' est exécuté dans un processus enfant distinct de bash qui est ignoré après exécution. Ce processus enfant est souvent appelé un sous-shell et peut être considéré comme un nouveau domaine d'application semblable à celui d'un enfant.

A titre d'exemple, considérons ce qui suit ...

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

 ~ # 

Notez que dans le premier exemple avec '{}', la variable est toujours définie même après la fermeture '}', alors que dans l'exemple avec '()', la variable n'est pas définie en dehors de la portée de '()'.

Fumée2345
la source
4

(...)sont utilisés pour exécuter du code dans un sous-shell. Le code utilisé {...}avant ne sera pas utilisé dans un sous-shell.

Antoine Orsoni
la source