Quando usar () vs. {} no bash?

74

Estou estudando scripts de shell com o bash e preciso saber a diferença entre (...)e {...}. Como alguém seleciona entre os dois ao escrever um script?

Mente Gorda
fonte
11
Veja wiki.bash-hackers.org
Helio
3
Você quis dizer apenas no contexto do agrupamento de comandos?
heemayl

Respostas:

87

Se você deseja que os efeitos colaterais da lista de comandos afetem seu shell atual , use {...}
Se você deseja descartar quaisquer efeitos colaterais, use(...)

Por exemplo, eu poderia usar um subshell se eu:

  • quero alterar $IFSpara alguns comandos, mas não quero alterar $IFSglobalmente para o shell atual
  • cdem algum lugar, mas não quero alterar o $PWDshell atual

Vale a pena notar que os parênteses podem ser usados ​​em uma definição de função:

  • uso normal: chaves: corpo da função é executado no shell atual; efeitos colaterais permanecem após a função ser concluída

    $ count_tmp() { cd /tmp; files=(*); echo "${#files[@]}"; }
    $ pwd; count_tmp; pwd
    /home/jackman
    11
    /tmp
    $ echo "${#files[@]}"
    11    
    
  • uso incomum: parênteses: o corpo da função é executado em um subshell; efeitos colaterais desaparecem quando o subshell sai

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

Documentação

Glenn Jackman
fonte
11
Após muitos anos de desenvolvimento de shell, eu não sabia que você poderia usar parênteses para executar funções em subshells. Que ótima idéia para evitar poluir o espaço para nome global!
L0b0:
7
O uso da localpalavra - chave ajuda bastante na limpeza dessa poluição.
glenn jackman
2
Sim, mas você deve se lembrar de declarar todas as variáveis ​​locais, e isso atrapalha o código.
L0b0
4
Dica: Se você deseja funções sem efeito colateral, mas evita a sintaxe incomum de declaração de função (da qual os editores de código podem não estar cientes), basta usar parênteses na chamada de função em vez da declaração:pwd; (count_tmp); pwd;
Juve
2
para o shell ... foo () (:;) é equivalente a foo () {(:;); } É assim que ele relata, se você perguntar!
Anthony
23

A partir da documentação oficial do bash :

()

( list )

Colocar uma lista de comandos entre parênteses causa a criação de um ambiente de subshell, e cada um dos comandos na lista é executado nesse subshell. Como a lista é executada em um subshell, as atribuições de variáveis ​​não permanecem em vigor após a conclusão do subshell.

{}

{ list; }

Colocar uma lista de comandos entre chaves faz com que a lista seja executada no contexto atual do shell. Nenhum subshell é criado. É necessário o ponto e vírgula (ou nova linha) a seguir.

Trauma Digital
fonte
9

O código em '{}' é executado no thread / processo / ambiente atual e as alterações são preservadas, para ser mais sucinto, o código é executado no escopo atual.
O código em '()' é executado dentro de um processo filho separado do bash que é descartado após a execução. Esse processo filho geralmente é chamado de subcasca e pode ser considerado como um novo escopo infantil.

Como exemplo, considere o seguinte ...

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

 ~ # 

Observe no primeiro exemplo com '{}' a variável ainda está definida mesmo após o fechamento '}', enquanto no exemplo com '()' a variável não está definida fora do escopo de '()'.

smokes2345
fonte
4

(...)são usados ​​para executar código em um sub-shell. O código usado bewteen {...}não será usado em um sub-shell.

Antoine Orsoni
fonte