Transformación automática de nuevas líneas en la asignación de variable de shell

Esto salió de uno de mis comentarios a esta pregunta con respecto al uso de bc en scripts de shell. bc pone saltos de línea en grandes numbers, por ejemplo:

 > num=$(echo 6^6^3 | bc) > echo $num 12041208676482351082020900568572834033367326934574532243581212211450\ 20555710636789704085475234591191603986789604949502079328192358826561\ 895781636115334656050057189523456 

Pero observe que en realidad no son saltos de línea en la variable, o al less no lo son si se usa sin comillas. Por ejemplo, al jugar con más pipa en la tarea, por ejemplo:

 num=$(echo 6^6^3 | bc | perl -pne 's/\\\n//g') 

Me di count de que, si bien hay una \n en la salida bc , la comprobación de echo $num > tmp.txt con hexdump muestra que \n (ASCII 10) se ha convertido definitivamente en un espacio (ASCII 32) en la asignación de variables.

O al less, en el resultado de $num > comillas. ¿Porqué es eso?

Como señala Fedorqui, si usas comillas: echo "$num" , obtienes nuevas líneas nuevamente. Esto es evidente al examinar la diferencia entre echo $num > tmp.1 y echo "$num" > tmp.2 con hexdump; el primero contiene \ (espacio de barra invertida) mientras que el último contiene \\n (línea invertida).

echo pone un espacio entre cada dos arguments. El intérprete de commands considera que la nueva línea en $num solo es un separador de palabras (al igual que el espacio).

 lines="a b c" set -x echo $lines # several arguments to echo echo "$lines" # one argument to echo 

Vea esta respuesta (por el propio OP) para una explicación más detallada.

No tengo idea de por qué está ahí, pero aquí está cómo deshabilitarlo con la implementación GNU de bc :

 echo '6^6^3' | BC_LINE_LENGTH=0 bc 

BC_LINE_LENGTH

Esto debería ser un número integer que especifica el número de caracteres en una línea de salida para numbers. Esto incluye la barra invertida y los caracteres de nueva línea para numbers largos. Como una extensión, el valor de cero desactiva la function de líneas múltiples. Cualquier otro valor de esta variable que sea menor que 3 establece la longitud de línea en 70.

Actualizar:

Estaba confundido acerca de esta pregunta, pensé que se trataba de los orígenes de la function de varias líneas, pero parece una extraña. De todos modos, la verdadera respuesta es que si no cita la variable, el intérprete de commands hará una split de palabras antes de pasar a echo . La split de palabras es el process donde una expansión se divide en 'palabras' según el contenido de IFS , estas 'palabras' se convierten en arguments diferentes. En el ejemplo de la pregunta, esto crea dos arguments para eco, que echo se separa con un espacio (lo sabía antes de que Stephane lo comentara, honestamente …).

Para evitar que esto ocurra, simplemente duplique la variable:

 num=$(echo '6^6^3' | bc) echo "$num" 

A veces, esto es realmente útil como una forma de eliminar los caracteres IFS de una variable (aunque printf %s es más seguro para cadenas arbitrarias). Ej. (En bash ):

 $ var=$'spaces: newlines:\n\n\ntabs:\t\t\t end' $ echo "$var" spaces: newlines: tabs: end $ newvar="$(printf '%s ' $var)" $ echo "$newvar" spaces: newlines: tabs: end 

Use tr para eliminar las continuidades de línea y las nuevas líneas:

 $ num=$(echo 6^6^3 | bc) $ echo "$num" 12041208676482351082020900568572834033367326934574532243581212211450\ 20555710636789704085475234591191603986789604949502079328192358826561\ 895781636115334656050057189523456 $ num=$(echo "$num" | tr -d '\n\\') $ echo "$num" 12041208676482351082020900568572834033367326934574532243581212211450205557106367897040854752345911916039867896049495020793281923588265618957816361153346560500571895 23456 

En la página man de bc, en expresiones, explica el límite.

Dado que los numbers son de precisión arbitraria, algunos numbers pueden no imprimirse en una sola línea de salida. Estos numbers largos se dividirán en líneas usando la "\" como el último carácter en una línea. La cantidad máxima de caracteres impresos por línea es 70.

Según el comentario de Stephane Chazelas , el problema no es exactamente que el espacio se interprete como una nueva línea. Lo ha explicado en otro lugar en relación con un operador (conceptual) de "split + glob", y aunque no sigo totalmente la semántica de eso, esto es lo que está sucediendo:

  • La variable sin cita se divide en espacios en blanco por el shell y estos se pasan como arguments para echo . La split elimina el espacio en blanco. Esto puede ser un poco confuso ya que "backslash-newline" parece una secuencia de escape, pero NO es equivalente a x=\\\n , es equivalente a x=$'\\\012' , \012 siendo octal para una nueva línea real, en oposition a la secuencia de escape \\n , que da como resultado que la cadena contenga un literal "\ n" (barra invertida-n), que no se dividiría.

  • echo luego saca sus arguments separados por un espacio.

Reiterar:

 str1=hello\\nworld str2=$'hello\012world' 

El intérprete de commands no dividirá el primero sin comillas ya que no contiene ningún espacio en blanco, contiene la secuencia de escape \n , mientras que en el segundo caso se dividirá sin comillas en el carácter de línea nueva real (que es el espacio en blanco).