Nueva línea en variables bash

9

Estoy tratando de almacenar varias líneas en una variable bash, pero parece que no funciona.

Por ejemplo, si enumero /binun archivo por línea y lo almaceno $LS, luego lo paso $LScomo stdin wc, siempre devuelve 1:

$ ls -1 /bin | wc -l
134
$ LS=$(ls -1 /bin); wc -l <<< $LS
1

Si intento imprimir en la pantalla, obtengo varios resultados: echoimprime todas las líneas en una sola línea, mientras printfimprime solo la primera línea:

#!/bin/bash
LS=$(ls -1 /bin)
echo $LS
printf $LS

Entonces, ¿una variable bash puede contener varias líneas?

Wizard79
fuente

Respuestas:

15

Necesita comillas dobles (y debe comillas dobles en la mayoría de los casos):

echo "$LS"

Pero no use echo para imprimir contenido de variables, en su lugar use printf :

printf '%s\n' "$LS"
Cuonglm
fuente
1
Solo para poner un punto más fino en la respuesta general: printf espera que una cadena de formato sea su primer parámetro, por lo que no vieron lo que esperaban. Si quieren ver nuevas líneas en la salida de printf, divididas arbitrariamente en espacios, printf "%s\n" $LSlo harían.
Jeff Schaller
el% LS debería ser $ LS, por cierto (no pude hacer una edición tan corta)
Jeff Schaller
¡Gracias! Por supuesto, también funciona con wcy otros comandos:wc -l <<< "$LS"
Wizard79
1
@JeffSchaller: No, en este caso no deberías quitar entre comillas la variable, solo usa printf '%s\n' "$LS"si quieres ver nueva línea. Eso está mal escrito, lo arregló!
cuonglm
¿Por qué no deberías usar echo para imprimir variables?
123
9

Las nuevas líneas están en la variable. LS=$(ls -1)establece la variable LSen la salida de ls -1(que produce la misma salida que ls, por cierto, excepto cuando la salida va a una terminal), menos las nuevas líneas finales.

El problema es que está eliminando las nuevas líneas cuando imprime el valor. En un script de shell, $LSno significa "el valor de la variable LS", significa "tomar el valor de LS, dividirlo en palabras según IFSe interpretar cada palabra como un patrón global". Para obtener el valor de LS, necesita escribir "$LS", o más generalmente poner $LSentre comillas dobles.

echo "$LS"imprime el valor de LS, excepto en algunos shells que interpretan caracteres de barra invertida, y a excepción de algunos valores que comienzan con -.

printf "$LS"imprime el valor LSsiempre que no contenga ningún carácter de porcentaje o barra invertida y (con la mayoría de las implementaciones) no comience con -.

Para imprimir el valor de LSexactamente, use printf %s "$LS". Si quieres una nueva línea al final, úsala printf '%s\n' "$LS".

Tenga en cuenta que $(ls)no es, en general, la lista de archivos en el directorio actual. Esto solo funciona cuando tienes nombres de archivo suficientemente mansos. Para obtener la lista de nombres de archivo (excepto archivos punto), es necesario utilizar un comodín: *. El resultado es una lista de cadenas, no una cadena, por lo que no puede asignarla a una variable de cadena; puede usar una variable de matriz files=(*)en shells que las admitan (ksh93, bash, zsh).

Para obtener más información, consulte ¿Por qué mi script de shell se ahoga en espacios en blanco u otros caracteres especiales?

Gilles 'SO- deja de ser malvado'
fuente
printf "$ LS" también sucumbiría al análisis de caracteres de barra diagonal inversa, similar al echo
cnst
0

Lo antes mencionado

printf '%s\n' "$LS"

Es la única solución correcta.

Permítanme demostrar que las otras soluciones propuestas, tanto con echoy printf, simplemente no funcionan correctamente:

$ mkdir t
$ cd t
$ touch \\n
$ LS=$(ls -l)
$ echo "$LS"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12

$ printf "$LS\n"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12

$ printf '%s\n' "$LS"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12 \n
$ ls -l
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12 \n
$ rm \\n
$ cd ..
$ rmdir t
$

(También se puede probar con V=$(printf 'line0:\\n\\n\nline1.\n') printf '%s\n' "$V"y variaciones).

Mientras que \nen los nombres de archivo puede no ser tan común, almacenar git diffen una variable, y sus posibilidades de encontrar literales \naumentan dramáticamente.

cnst
fuente
Tenga en cuenta que kshy zshtambién soporte print -r -- "$LS"y zshtambién tiene echo -E - $LS. cshtiene echo $LS:qsin embargo que no emitirá el carácter de nueva línea Si $ LS está vacía, y conseguir un valor de varias líneas en $ LS en el primer lugar es bastante complicado en CSH.
Stéphane Chazelas