$(…)é um subshell por definição: é uma cópia do estado de tempo de execução do shell¹, e as alterações no estado feito no subshell não têm impacto no pai. Um subshell é tipicamente implementado bifurcando um novo processo (mas alguns shells podem otimizar isso em alguns casos).
Não é um subshell do qual você pode recuperar valores variáveis. Se as alterações nas variáveis tivessem um impacto sobre o pai, não seria um subshell. É um subshell cuja saída o pai pode recuperar. O subshell criado por $(…)tem sua saída padrão definida como um canal, e o pai lê esse canal e coleta a saída.
Existem várias outras construções que criam um subshell. Eu acho que esta é a lista completa do bash:
Subshell para agrupamento : ( … )não faz nada além de criar um subshell e aguarde o término). Contraste com o { … }qual os grupos comandam puramente para fins sintáticos e não cria um subshell.
… &Segundo plano : cria um subshell e não espera que ele termine.
Pipeline : … | …cria dois subshells, um para o lado esquerdo e outro para o lado direito, e aguarda o término de ambos. O invólucro cria um tubo e conecta a saída padrão do lado esquerdo à extremidade de gravação do tubo e a entrada padrão do lado direito à extremidade de leitura. Em alguns shells (ksh88, ksh93, zsh, bash com a lastpipeopção configurada e efetiva), o lado direito é executado no shell original, portanto, a construção do pipeline cria apenas um subshell.
Substituição de comando : $(…)(também escrito `…`) cria um subshell com sua saída padrão definida como um pipe, coleta a saída no pai e expande para essa saída, menos suas novas linhas finais. (E a saída pode estar ainda mais sujeita a divisão e globbing, mas isso é outra história.)
Substituição de processo : <(…)cria um subshell com sua saída padrão definida como um pipe e se expande para o nome do pipe. O pai (ou algum outro processo) pode abrir o canal para se comunicar com o subshell. >(…)faz o mesmo, mas com o tubo na entrada padrão.
Coprocesso : coproc …cria um subshell e não espera que ele seja finalizado. A entrada e saída padrão do subshell são definidas para um tubo com o pai conectado à outra extremidade de cada tubo.
@ user1717828 O que? Por quê? O que a expansão variável remotamente tem a ver com essa pergunta? Não vou incluir todo o manual do shell na minha resposta.
Gilles 'SO- stop be evil'
11
O que a expansão variável remotamente tem a ver com essa pergunta? Não sei, foi por isso que perguntei :-) Então, acho que a substituição da chave não é como a substituição da chave entre parênteses.
User1717828
@ user1717828: a expansão variável não está relacionada aos subshells; é um mecanismo completamente separado, e definitivamente vale a pena ler se você está apenas começando!
0xdd
11
@EnricoMariaDeAngelis Não é o único caminho, mas é o caminho mais natural. Outra maneira é command | { read line; … }(dependendo do shell, ainda linepode ou não estar disponível após o pipeline). Todas as formas envolvem um subshell porque o comando que produz a saída precisa ser executado independentemente do shell que lê a entrada. Se o comando for puramente interno ao shell (apenas construções e componentes do shell, sem comandos externos), o shell pode não criar um subprocesso, mas isso é apenas uma otimização, mas ainda cria um subshell.
Gilles 'SO- stop be evil'
20
Na página do manual bash (1) na versão bash 4.4, seção "EXPANSÃO", subseção "Substituição de Comando":
Bash realiza a expansão executando commandem um ambiente subshell [...]
Curiosamente, no CentOS 7, a página de bashmanual não menciona nenhum subconjunto: Bash performs the expansion by executing command and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.eu me pergunto se isso foi uma omissão deliberada.
dr01
6
@ dr01 Pelo contrário, o bash 4.4 mudou a redação dessa frase para incluir a palavra “subshell”. Foi um esclarecimento: o manual mencionou explicitamente que várias outras construções eram subcamadas, mas até o 4.4 não era explicitamente indicado para substituição de comando.
Gilles 'SO- stop be evil'
Sim, no CentOS v7.4.1708 (relativamente recente) o bash é a v4.2.46.
Dr01
5
Sim, ( commands... )é um bashsubshell que será executado commands...em outro processo.
A única diferença que você tem $( commands... )é que essa parte do código será commands...substituída , após a execução, por tudo o que você commands...escreveu stdout.
Respostas:
$(…)
é um subshell por definição: é uma cópia do estado de tempo de execução do shell¹, e as alterações no estado feito no subshell não têm impacto no pai. Um subshell é tipicamente implementado bifurcando um novo processo (mas alguns shells podem otimizar isso em alguns casos).Não é um subshell do qual você pode recuperar valores variáveis. Se as alterações nas variáveis tivessem um impacto sobre o pai, não seria um subshell. É um subshell cuja saída o pai pode recuperar. O subshell criado por
$(…)
tem sua saída padrão definida como um canal, e o pai lê esse canal e coleta a saída.Existem várias outras construções que criam um subshell. Eu acho que esta é a lista completa do bash:
( … )
não faz nada além de criar um subshell e aguarde o término). Contraste com o{ … }
qual os grupos comandam puramente para fins sintáticos e não cria um subshell.… &
Segundo plano : cria um subshell e não espera que ele termine.… | …
cria dois subshells, um para o lado esquerdo e outro para o lado direito, e aguarda o término de ambos. O invólucro cria um tubo e conecta a saída padrão do lado esquerdo à extremidade de gravação do tubo e a entrada padrão do lado direito à extremidade de leitura. Em alguns shells (ksh88, ksh93, zsh, bash com alastpipe
opção configurada e efetiva), o lado direito é executado no shell original, portanto, a construção do pipeline cria apenas um subshell.$(…)
(também escrito`…`
) cria um subshell com sua saída padrão definida como um pipe, coleta a saída no pai e expande para essa saída, menos suas novas linhas finais. (E a saída pode estar ainda mais sujeita a divisão e globbing, mas isso é outra história.)<(…)
cria um subshell com sua saída padrão definida como um pipe e se expande para o nome do pipe. O pai (ou algum outro processo) pode abrir o canal para se comunicar com o subshell.>(…)
faz o mesmo, mas com o tubo na entrada padrão.coproc …
cria um subshell e não espera que ele seja finalizado. A entrada e saída padrão do subshell são definidas para um tubo com o pai conectado à outra extremidade de cada tubo.¹ Em vez de executar um shell separado .
fonte
${...}
na resposta?command | { read line; … }
(dependendo do shell, aindaline
pode ou não estar disponível após o pipeline). Todas as formas envolvem um subshell porque o comando que produz a saída precisa ser executado independentemente do shell que lê a entrada. Se o comando for puramente interno ao shell (apenas construções e componentes do shell, sem comandos externos), o shell pode não criar um subprocesso, mas isso é apenas uma otimização, mas ainda cria um subshell.Na página do manual bash (1) na versão bash 4.4, seção "EXPANSÃO", subseção "Substituição de Comando":
fonte
bash
manual não menciona nenhum subconjunto:Bash performs the expansion by executing command and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.
eu me pergunto se isso foi uma omissão deliberada.Sim,
( commands... )
é umbash
subshell que será executadocommands...
em outro processo.A única diferença que você tem
$( commands... )
é que essa parte do código serácommands...
substituída , após a execução, por tudo o que vocêcommands...
escreveustdout
.fonte