Diferencia entre 'ls' y 'echo $ (ls)'

27

Considere las dos muestras de concha

$ ls
myDoc.html
SomeDirectory
someDoc.txt

y

$ echo $(ls)
myDoc.html SomeDirectory someDoc.txt

El primero se ejecuta lsy, según tengo entendido, agrega el contenido del directorio de trabajo actual al stdoutarchivo (que es lo que muestra el terminal). ¿Es esto correcto?

El segundo toma el valor del lscomando (lo que significa que es el contenido del directorio de trabajo actual) y lo imprime en el stdoutarchivo. ¿Es esto correcto?

¿Por qué los dos comandos dan diferentes salidas?

Owen
fuente
1
porque estás haciendo eco La salida de ls como entrada al comando echo.
ankidaemon
Este es el comportamiento "esperado", que alguien explicará en breve. Sin embargo, ¿está seguro de que lspor sí solo no le da múltiples artículos por línea?
roaima
@roaima columnized ls es un remanente de una "característica" bsd. las versiones de Unix imprimen una entrada por línea
Steve Cox
@SteveCox No he usado UNIX apropiado desde principios de SVR4, así que mi memoria es un poco confusa. Gracias por el recordatorio.
roaima
¿Por qué no echo *?
jdh8

Respuestas:

70

Cuando ejecutas este comando:

ls

el terminal muestra la salida de ls.

Cuando ejecutas este comando:

echo $(ls)

el shell captura la salida $(ls)y realiza la división de palabras en él. Con el valor predeterminado IFS, esto significa que todas las secuencias de espacios en blanco, incluidos los caracteres de nueva línea, se reemplazan por un solo espacio en blanco. Es por eso que la salida de echo $(ls)aparece en una línea.

Para una discusión avanzada sobre la división de palabras, vea las preguntas frecuentes de Greg .

Suprimir la división de palabras

El shell no realiza la división de palabras en cadenas entre comillas. Por lo tanto, puede suprimir la división de palabras y retener la salida multilínea con:

echo "$(ls)"

ls y salida multilínea

Es posible que haya notado que a lsveces imprime más de un archivo por línea:

$ ls
file1  file2  file3  file4  file5  file6

Este es el valor predeterminado cuando la salida de lsva a un terminal. Cuando la salida no va directamente a una terminal, lscambia su valor predeterminado a un archivo por línea:

$ echo "$(ls)"
file1
file2
file3
file4
file5
file6

Este comportamiento está documentado en man ls.

Otra sutileza: sustitución de comandos y líneas nuevas

$(...)es la sustitución de comandos y el shell elimina los caracteres de línea nueva del resultado de la sustitución de comandos . Esto normalmente no se nota porque, por defecto, echoagrega una nueva línea al final de su salida. Por lo tanto, si pierde una nueva línea desde el final $(...)y gana una echo, no hay cambio. Sin embargo, si la salida de su comando termina con 2 o más caracteres de nueva línea y echoagrega solo uno, a su salida le faltarán una o más nuevas líneas. Como ejemplo, podemos usar printfpara generar caracteres de nueva línea finales. Tenga en cuenta que los dos comandos siguientes, a pesar del número diferente de líneas nuevas, producen el mismo resultado de una línea en blanco:

$ echo "$(printf "\n")"

$ echo "$(printf "\n\n\n\n\n")"

$ 

Este comportamiento está documentado en man bash.

Otra sorpresa: expansión del nombre de ruta, dos veces

Vamos a crear tres archivos:

$ touch 'file?' file1 file2

Observe la diferencia entre ls file?y echo $(ls file?):

$ ls file?
file?  file1  file2
$ echo $(ls file?)
file? file1 file2 file1 file2

En el caso de echo $(ls file?)la glob archivo file?se expande en dos ocasiones , haciendo que los nombres de archivo file1y file2de aparecer dos veces en la salida. Esto se debe a que, como señala Jeffiekins, la expansión del nombre de ruta se realiza primero por el shell antes de lsejecutarse y luego nuevamente antes de echoejecutarse.

La segunda expansión del nombre de ruta se puede suprimir si usamos comillas dobles:

$ echo "$(ls file?)"
file?
file1
file2
John1024
fuente
44
adicionalmente, usando isattypuede verificar si stdout es un tty, ls usa esto para determinar si usar colores o no.
Janus Troelsen
1
¿Las citas no coinciden en el último fragmento de código? ¿O en $( )realidad proporciona una barrera de sintaxis para que no necesite interpolar?
gato
66
@cat $()proporciona una barrera de sintaxis.
Random832
2
Una diferencia más importante: si algún nombre de archivo contiene un carácter comodín, el echocomando expandirá el comodín . Por ejemplo, si lsdevuelve el archivo? file1 file2 , entonces echo $(ls)devolverá el archivo? archivo1 archivo2 archivo1 archivo2 .
Jeffiekins
1
@Jeffiekins echono expande comodines; el shell lo hace antes de pasar la expansión resultante como argumentos a echo.
chepner
0

ls sabe si está enviando salida a un terminal o no.

Ejecutar lsen un símbolo del sistema escribirá en su pseudo-tty. Redirigir la salida de lsgeneralmente no irá a un pseudo-tty, y lsformateará la salida de manera diferente.

mpez0
fuente