O shell deve expandir a substituição do comando executando o comando em um ambiente subshell (consulte Shell Execution Environment ) e substituindo a substituição do comando (o texto do comando mais as aspas "$()") pela saída padrão do comando, removendo sequências de um ou mais <newline> caracteres no final da substituição.
No entanto, observar o comportamento real dos shells revela algumas surpresas (bem, uma). Estou usando um script contendo apenas
echo \
Before \
`# commented` \
After
Bash e Zsh bifurcam um subshell para executar a substituição do comando “vazio”:
$ strace -f -e process bash bttest
execve("/bin/bash", ["bash", "bttest"], 0x7ffe31522a10 /* 67 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDstrace: Process 3134851 attached
, child_tidptr=0x7fe3ef0c6a10) = 3134851
[pid 3134851] exit_group(0) = ?
[pid 3134851] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3134851, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG, NULL) = 3134851
wait4(-1, 0x7ffecdd13810, WNOHANG, NULL) = -1 ECHILD (No child processes)
Before After
exit_group(0) = ?
+++ exited with 0 +++
$ strace -f -e process zsh bttest
execve("/usr/bin/zsh", ["zsh", "bttest"], 0x7fffa2f78140 /* 67 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDstrace: Process 3134903 attached
, child_tidptr=0x7f236ce63750) = 3134903
[pid 3134903] exit_group(0) = ?
[pid 3134902] kill(3134903, 0) = 0
[pid 3134903] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3134903, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED|WCONTINUED, {ru_utime={tv_sec=0, tv_usec=593}, ru_stime={tv_sec=0, tv_usec=0}, ...}) = 3134903
wait4(-1, 0x7ffcabc4a7d4, WNOHANG|WSTOPPED|WCONTINUED, 0x7ffcabc4a7f0) = -1 ECHILD (No child processes)
kill(3134903, 0) = -1 ESRCH (No such process)
Before After
exit_group(0) = ?
+++ exited with 0 +++
O DASH, por outro lado, analisa o conteúdo dentro dos crases para determinar se eles representam um comando interno ou um comando externo, e se essa análise resultar em um “nó” vazio (um comando vazio), ignora-o completamente :
$ strace -f -e process dash bttest
execve("/bin/dash", ["dash", "bttest"], 0x7ffe61eee5c0 /* 67 vars */) = 0
Before After
exit_group(0) = ?
+++ exited with 0 +++
Portanto, existe pelo menos um shell “inteligente” o suficiente para não lançar um subshell neste cenário.
(Não existe um “shell POSIX” - POSIX é uma especificação, sem implementações de referência. Nenhum shell que eu conheça implementa estritamente POSIX e apenas POSIX.)
Como escreve ilkkachu , o próprio POSIX especifica que
No entanto, observar o comportamento real dos shells revela algumas surpresas (bem, uma). Estou usando um script contendo apenas
Bash e Zsh bifurcam um subshell para executar a substituição do comando “vazio”:
O DASH, por outro lado, analisa o conteúdo dentro dos crases para determinar se eles representam um comando interno ou um comando externo, e se essa análise resultar em um “nó” vazio (um comando vazio), ignora-o completamente :
Portanto, existe pelo menos um shell “inteligente” o suficiente para não lançar um subshell neste cenário.
(Não existe um “shell POSIX” - POSIX é uma especificação, sem implementações de referência. Nenhum shell que eu conheça implementa estritamente POSIX e apenas POSIX.)