tl; dr
A única stuff
seria provavelmente trabalhar para você.
Resposta completa
O que acontece
Quando você executa foo $(stuff)
, é isso que acontece:
stuff
corre;
- sua saída (stdout), em vez de ser impressa, substitui
$(stuff)
na chamada de foo
;
- depois
foo
é executado, seus argumentos de linha de comando obviamente dependem do que stuff
retornou.
Esse $(…)
mecanismo é chamado "substituição de comando". No seu caso, o comando principal é o echo
que basicamente imprime seus argumentos de linha de comando no stdout. Portanto, tudo o que stuff
tenta imprimir no stdout é capturado, passado echo
e impresso no stdout echo
.
Se você deseja que a saída stuff
seja impressa em stdout, basta executar a sola stuff
.
A `…`
sintaxe serve ao mesmo propósito que $(…)
(sob o mesmo nome: "substituição de comando"), porém existem poucas diferenças, portanto você não pode trocá-las cegamente. Veja este FAQ e esta pergunta .
Devo evitar, echo $(stuff)
não importa o quê?
Há uma razão que você pode querer usar echo $(stuff)
se souber o que está fazendo. Pela mesma razão, você deve evitar echo $(stuff)
se realmente não sabe o que está fazendo.
O ponto é stuff
e echo $(stuff)
não é exatamente equivalente. O último significa chamar o operador split + glob na saída de stuff
com o valor padrão de $IFS
. Citar duas vezes a substituição de comando evita isso. A citação única da substituição de comando faz com que não seja mais uma substituição de comando.
Para observar isso quando se trata de divisão, execute estes comandos:
echo "a b"
echo $(echo "a b")
echo "$(echo "a b")" # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "a b")'
E para globbing:
echo "/*"
echo $(echo "/*")
echo "$(echo "/*")" # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "/*")'
Como você pode ver, echo "$(stuff)"
é equivalente (-ish *) a stuff
. Você poderia usá-lo, mas qual é o objetivo de complicar as coisas dessa maneira?
Por outro lado, se você quer a saída de stuff
se submeter a divisão + englobamento, então você pode encontrar echo $(stuff)
útil. Tem que ser sua decisão consciente.
Existem comandos que geram resultados que devem ser avaliados (incluindo divisão, globbing e mais) e executados pelo shell, portanto, eval "$(stuff)"
é uma possibilidade (consulte esta resposta ). Eu nunca vi um comando que precise que sua saída sofra divisão + globbing adicionais antes de ser impressa . O uso deliberado echo $(stuff)
parece muito incomum.
Que tal var=$(stuff); echo "$var"
?
Bom ponto. Este trecho:
var=$(stuff)
echo "$var"
deve ser equivalente a echo "$(stuff)"
(-ish *) a stuff
. Se for o código inteiro, basta executar stuff
.
Se, no entanto, você precisar usar a saída de stuff
mais de uma vez, essa abordagem
var=$(stuff)
foo "$var"
bar "$var"
geralmente é melhor que
foo "$(stuff)"
bar "$(stuff)"
Mesmo que foo
seja echo
e você entra echo "$var"
em seu código, pode ser melhor mantê-lo dessa maneira. Coisas a considerar:
- Com
var=$(stuff)
stuff
corre uma vez; mesmo que o comando seja rápido, evitar a computação da mesma saída duas vezes é a coisa certa. Ou talvez stuff
tenha outros efeitos além de gravar no stdout (por exemplo, criar um arquivo temporário, iniciar um serviço, iniciar uma máquina virtual, notificar um servidor remoto), para que você não queira executá-lo várias vezes.
- Se
stuff
gerar uma saída dependente do tempo ou algo aleatória, você poderá obter resultados inconsistentes de foo "$(stuff)"
e bar "$(stuff)"
. Após var=$(stuff)
o valor de $var
ser corrigido, você pode ter certeza foo "$var"
e bar "$var"
obter um argumento de linha de comando idêntico.
Em alguns casos, em vez de foo "$var"
você querer (precisar) usar foo $var
, especialmente se stuff
gerar vários argumentos para foo
(uma variável de matriz pode ser melhor se o seu shell suportar). Mais uma vez, saiba o que está fazendo. Quando se trata echo
da diferença entre echo $var
e echo "$var"
é o mesmo que entre echo $(stuff)
e echo "$(stuff)"
.
* Equivalente ( -ish )?
Eu disse que echo "$(stuff)"
é equivalente (-ish) a stuff
. Há pelo menos dois problemas que o tornam não exatamente equivalente:
$(stuff)
é executado stuff
em um subshell, então é melhor dizer que echo "$(stuff)"
é equivalente (-ish) a (stuff)
. Os comandos que afetam o shell em que são executados, se estiverem em um subshell, não afetam o shell principal.
Neste exemplo stuff
é a=1; echo "$a"
:
a=0
echo "$(a=1; echo "$a")" # echo "$(stuff)"
echo "$a"
Compare com
a=0
a=1; echo "$a" # stuff
echo "$a"
e com
a=0
(a=1; echo "$a") # (stuff)
echo "$a"
Outro exemplo, comece stuff
sendo cd /; pwd
:
cd /bin
echo "$(cd /; pwd)" # echo "$(stuff)"
pwd
e teste stuff
e (stuff)
versões.
echo
não é uma boa ferramenta para exibir dados não controlados . Isso echo "$var"
estávamos falando sobre deveria ter sido printf '%s\n' "$var"
. Mas como a pergunta menciona echo
e como a solução mais provável é não usar echo
em primeiro lugar, decidi não apresentar printf
até agora.
stuff
ou (stuff)
intercalará a saída stdout e stderr, enquanto echo $(stuff)
imprimirá toda a saída stderr de stuff
(que roda primeiro) e somente então a saída stdout digerida por echo
(que roda por último).
$(…)
retira qualquer nova linha à direita e a echo
adiciona novamente. Então, echo "$(printf %s 'a')" | xxd
dá saída diferente de printf %s 'a' | xxd
.
Alguns comandos ( ls
por exemplo) funcionam de maneira diferente, dependendo se a saída padrão é um console ou não; por isso ls | cat
não o mesmo ls
faz. Da mesma forma echo $(ls)
irá funcionar de forma diferente do que ls
.
Deixando de ls
lado, em um caso geral, se você precisar forçar esse outro comportamento, stuff | cat
é melhor echo $(ls)
ou echo "$(ls)"
porque não aciona todos os outros problemas mencionados aqui.
Possivelmente status de saída diferente (mencionado para completar esta resposta da wiki; para detalhes, veja outra resposta que merece crédito).