tl; dr
Una suela stuff
sería más probable que funcione para usted.
Respuesta completa
Lo que pasa
Cuando corres foo $(stuff)
, esto es lo que sucede:
stuff
carreras;
- su salida (stdout), en lugar de imprimirse, reemplaza
$(stuff)
en la invocación de foo
;
- luego se
foo
ejecuta, sus argumentos de línea de comando obviamente dependen de lo que stuff
devuelve.
Este $(…)
mecanismo se llama "sustitución de comando". En su caso, el comando principal es el echo
que básicamente imprime sus argumentos de línea de comando en stdout. Entonces, cualquier cosa que stuff
intente imprimir en stdout es capturada, pasada echo
e impresa en stdout por echo
.
Si desea que la salida stuff
se imprima en stdout, simplemente ejecute la suela stuff
.
La `…`
sintaxis tiene el mismo propósito que $(…)
(bajo el mismo nombre: "sustitución de comandos"), aunque existen pocas diferencias, por lo que no puede intercambiarlas a ciegas. Vea estas preguntas frecuentes y esta pregunta .
¿Debo evitar echo $(stuff)
pase lo que pase?
Es posible que desee utilizar una razón echo $(stuff)
si sabe lo que está haciendo. Por la misma razón que debes evitar echo $(stuff)
si realmente no sabes lo que estás haciendo.
El punto es stuff
y echo $(stuff)
no es exactamente equivalente. Esto último significa llamar al operador split + glob en la salida de stuff
con el valor predeterminado de $IFS
. Citar dos veces la sustitución del comando evita esto. Citar solo la sustitución de comandos hace que ya no sea una sustitución de comandos.
Para observar esto cuando se trata de dividir, ejecute estos 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")'
Y para globbing:
echo "/*"
echo $(echo "/*")
echo "$(echo "/*")" # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "/*")'
Como puede ver, echo "$(stuff)"
es equivalente (-ish *) a stuff
. Podrías usarlo, pero ¿de qué sirve complicar las cosas de esta manera?
Por otro lado, si desea que la salida se stuff
divida + globbing, entonces puede resultarle echo $(stuff)
útil. Sin embargo, tiene que ser tu decisión consciente.
Hay comandos que generan resultados que deben evaluarse (lo que incluye división, globalización y más) y ejecutarse por el shell, por lo que eval "$(stuff)"
es una posibilidad (vea esta respuesta ). Nunca he visto un comando que necesita su salida para someterse a división adicional + globbing antes de imprimirse . El uso deliberado echo $(stuff)
parece muy poco común.
¿Qué hay de var=$(stuff); echo "$var"
?
Buen punto. Este fragmento:
var=$(stuff)
echo "$var"
debe ser equivalente a echo "$(stuff)"
equivalente (-ish *) a stuff
. Si es todo el código, simplemente ejecute en su stuff
lugar.
Sin embargo, si necesita usar la salida de stuff
más de una vez, entonces este enfoque
var=$(stuff)
foo "$var"
bar "$var"
suele ser mejor que
foo "$(stuff)"
bar "$(stuff)"
Incluso si foo
es así echo
y obtiene echo "$var"
su código, puede ser mejor mantenerlo de esta manera. Cosas para considerar:
- Con
var=$(stuff)
stuff
carreras una vez; incluso si el comando es rápido, evitar calcular la misma salida dos veces es lo correcto. O tal vez stuff
tenga otros efectos además de escribir en stdout (por ejemplo, crear un archivo temporal, iniciar un servicio, iniciar una máquina virtual, notificar a un servidor remoto), por lo que no desea ejecutarlo varias veces.
- Si
stuff
genera resultados dependientes del tiempo o algo aleatorios, puede obtener resultados inconsistentes de foo "$(stuff)"
y bar "$(stuff)"
. Después de que var=$(stuff)
el valor de $var
es fijo y puede estar seguro foo "$var"
y bar "$var"
obtener un argumento de línea de comando idéntico.
En algunos casos, en lugar de foo "$var"
que desee (necesite) usar foo $var
, especialmente si stuff
genera múltiples argumentos para foo
(una variable de matriz puede ser mejor si su shell lo admite). De nuevo, sepa lo que está haciendo. Cuando se trata de echo
la diferencia entre echo $var
y echo "$var"
es lo mismo que entre echo $(stuff)
y echo "$(stuff)"
.
* Equivalente ( -ish )?
Dije que echo "$(stuff)"
es equivalente (-ish) a stuff
. Hay al menos dos problemas que hacen que no sea exactamente equivalente:
$(stuff)
se ejecuta stuff
en una subshell, por lo que es mejor decir que echo "$(stuff)"
es equivalente (-ish) a (stuff)
. Los comandos que afectan el shell en el que se ejecutan, si están en un subshell, no afectan al shell principal.
En este ejemplo stuff
es a=1; echo "$a"
:
a=0
echo "$(a=1; echo "$a")" # echo "$(stuff)"
echo "$a"
Compáralo con
a=0
a=1; echo "$a" # stuff
echo "$a"
y con
a=0
(a=1; echo "$a") # (stuff)
echo "$a"
Otro ejemplo, comienza con stuff
ser cd /; pwd
:
cd /bin
echo "$(cd /; pwd)" # echo "$(stuff)"
pwd
y prueba stuff
y (stuff)
versiones.
echo
No es una buena herramienta para mostrar datos no controlados . Esto de lo echo "$var"
que estábamos hablando debería haber sido printf '%s\n' "$var"
. Pero como la pregunta menciona echo
y dado que la solución más probable es no usarla echo
en primer lugar, decidí no presentarla printf
hasta ahora.
stuff
o (stuff)
intercalará la salida stdout y stderr, mientras echo $(stuff)
imprimirá toda la salida stderr de stuff
(que se ejecuta primero), y solo luego la salida stdout digerida por echo
(que se ejecuta al final).
$(…)
elimina cualquier nueva línea final y luego la echo
agrega de nuevo. Entonces echo "$(printf %s 'a')" | xxd
da una salida diferente que printf %s 'a' | xxd
.
Algunos comandos ( ls
por ejemplo) funcionan de manera diferente dependiendo de si la salida estándar es una consola o no; así ls | cat
que no hace lo mismo ls
. Del mismo modo echo $(ls)
funcionará de manera diferente que ls
.
Dejando de ls
lado, en un caso general, si tiene que forzar este otro comportamiento, entonces stuff | cat
es mejor echo $(ls)
o echo "$(ls)"
porque no desencadena todos los otros problemas mencionados aquí.
Posiblemente diferente estado de salida (mencionado para completar la respuesta de esta wiki; para más detalles vea otra respuesta que merece crédito).