¿Cómo puedo hacer que el operador bash backtick mantenga nuevas líneas en la salida?


36

¿Hay alguna manera de hacer que bash no coma nuevas líneas en la sustitución de backtick?

Por ejemplo:

var=`echo line one && echo line two`
echo $var

line one line two

Lo que quiero es

var=`echo line one && echo line two` # plus some magic
echo $var

line one
line two

Respuestas:


54

No es un problema con la sustitución de backticks, sino con echo; debe citar la variable para que los caracteres de control funcionen:

$ var=`echo line one && echo line two`
$ echo "$var"
line one
line two

Entonces, bash elimina las nuevas líneas mientras expande una variable en el comando-gravamen? ¿Y ese comportamiento se puede deshabilitar citando la variable?
slowpoison

77
bashrealiza parámetros de expansión antes de ejecutar un comando, citando es necesario informar bashque desea imprimir una sola cadena y no palabras (4 line, one, liney two). Prueba: echo a <many spaces> by echo "a <many spaces> b". Echa un vistazo a esta respuesta también.
cYrus

5

Aunque @cyrus es correcto, en realidad no responde a toda la pregunta, y no hay una explicación de lo que está sucediendo.

Así que vamos a atravesarlo.

Nuevas líneas en cadena

Primero determine la secuencia de bytes que se espera:

$ { echo a; echo b; } | xxd
0000000: 610a 620a                                a.b.

Ahora, use la sustitución de comandos (Sección 3.5.4) para intentar capturar esta secuencia de bytes:

$ x=$( echo a; echo b; )

Luego, haga un eco simple para verificar la secuencia de bytes:

$ echo $x | xxd
0000000: 6120 620a                                a b.

Parece que la primera línea nueva fue reemplazada por un espacio, y la segunda línea nueva quedó intacta. Pero por qué ?

Veamos lo que realmente está sucediendo aquí:

Primero, bash hará la expansión de parámetros de Shell (Sección 3.5.3)

El carácter '$' introduce la expansión de parámetros, la sustitución de comandos o la expansión aritmética. El nombre o símbolo del parámetro a expandir puede estar encerrado entre llaves, que son opcionales pero sirven para proteger la variable a expandir de los caracteres inmediatamente posteriores que podrían interpretarse como parte del nombre.

Luego bash hará la división de palabras (Sección 3.5.7)

El shell escanea los resultados de la expansión de parámetros, la sustitución de comandos y la expansión aritmética que no ocurrieron entre comillas dobles para la división de palabras.

El shell trata cada carácter de $ IFS como un delimitador, y divide los resultados de las otras expansiones en palabras sobre estos caracteres. Si IFS no está configurado, o su valor es exactamente, ...

A continuación, bash lo tratará como un comando simple (Sección 3.2.1)

Un comando simple es el tipo de comando que se encuentra con mayor frecuencia. Es solo una secuencia de palabras separadas por espacios en blanco, terminadas por uno de los operadores de control del shell (ver Definiciones). La primera palabra generalmente especifica un comando para ser ejecutado, y el resto de las palabras son los argumentos de ese comando.

La definición de espacios en blanco (Sección 2 - Definiciones)

en blanco Un espacio o un carácter de tabulación.

Finalmente, bash invoca el comando interno echo (Sección 4.2 - Bash Builtin Commands)

... Emite los argumentos, separados por espacios, terminados en una nueva línea. ...

Para resumir, las nuevas líneas se eliminan mediante Word Splitting, y luego echo obtiene 2 args, "a" y "b", y luego los separa por espacios y termina con una nueva línea.

Al hacer lo que @cyrus sugirió (y suprimir la nueva línea del eco con -n), el resultado es mejor:

$ echo -n "$x" | xxd
0000000: 610a 62                                  a.b

Líneas nuevas al final de la cadena

Sin embargo, todavía no es perfecto, la nueva línea final se ha ido. Mirando más de cerca la sustitución de comandos (Sección 3.5.4) :

Bash realiza la expansión ejecutando el comando y reemplazando la sustitución del comando con la salida estándar del comando, con cualquier nueva línea final eliminada.

Ahora que está claro por qué la nueva línea desaparece, se puede engañar a bash para que la conserve. Para hacer esto, agregue una cadena adicional al final y elimínela cuando use la variable:

$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a                                a.b.

TL; DR

Agregue una parte adicional al final de la salida y cite las variables:

$ cat /no/file/here 2>&1 | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ cat /no/file/here 2>&1 | cksum
3561909523 46
$ 
$ var=$( cat /no/file/here 2>&1; rc=${?}; echo extra; exit ${rc})
$ echo $?
1
$ 
$ echo -n "${var%extra}" | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ echo -n "${var%extra}" | cksum
3561909523 46

0

Hubiera puesto mi pequeña adición en un comentario sobre la respuesta de cYrus, pero todavía no puedo comentar sobre las respuestas que creo. Así que duplicaré parte de cYrus su respuesta para completar.

Lo que sucede en la primera línea de su código es que, de hecho, la sustitución de backticks come las nuevas líneas finales de la salida del comando sustituido, como se documenta . Pero no se come la nueva línea entre sus dos líneas.

Lo que sucede en la segunda línea de código es que echo se ejecuta con dos argumentos, la primera y la segunda línea. Citar la variable expandida corregirá eso: echo "$var"Esto preservará la nueva línea entre su línea uno y la línea dos. Y echoagregará una nueva línea final de forma predeterminada para que todo esté bien.

Ahora, si desea conservar todas las nuevas líneas finales en la salida de alguna sustitución de comando, una solución alternativa es agregar otra línea a la salida, que puede eliminar después de la sustitución de comando, consulte aquí .

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.