¿Cómo puedo tener una nueva línea en una cadena en sh?

340

Esta

STR="Hello\nWorld"
echo $STR

produce como salida

Hello\nWorld

en vez de

Hello
World

¿Qué debo hacer para tener una nueva línea en una cadena?

Nota: Esta pregunta no se trata de echo . Soy consciente de ello echo -e, pero estoy buscando una solución que permita pasar una cadena (que incluye una nueva línea) como argumento a otros comandos que no tienen una opción similar para interpretarla \ncomo nuevas líneas.

Juan A. Navarro
fuente

Respuestas:

354

La solución es usar $'string', por ejemplo:

$ STR=$'Hello\nWorld'
$ echo "$STR" # quotes are required here!
Hello
World

Aquí hay un extracto de la página del manual de Bash:

   Words of the form $'string' are treated specially.  The word expands to
   string, with backslash-escaped characters replaced as specified by  the
   ANSI  C  standard.  Backslash escape sequences, if present, are decoded
   as follows:
          \a     alert (bell)
          \b     backspace
          \e
          \E     an escape character
          \f     form feed
          \n     new line
          \r     carriage return
          \t     horizontal tab
          \v     vertical tab
          \\     backslash
          \'     single quote
          \"     double quote
          \nnn   the eight-bit character whose value is  the  octal  value
                 nnn (one to three digits)
          \xHH   the  eight-bit  character  whose value is the hexadecimal
                 value HH (one or two hex digits)
          \cx    a control-x character

   The expanded result is single-quoted, as if the  dollar  sign  had  not
   been present.

   A double-quoted string preceded by a dollar sign ($"string") will cause
   the string to be translated according to the current  locale.   If  the
   current  locale  is  C  or  POSIX,  the dollar sign is ignored.  If the
   string is translated and replaced, the replacement is double-quoted.
anfetamaquina
fuente
74
Eso es válido bash, pero no POSIX sh.
jmanning2k
77
+ 1: solo una nota: export varDynamic = "$ varAnother1 $ varAnother2" $ '\ n'
Yordan Georgiev
3
De hecho, esto funciona con cadenas literales como lo solicitó el OP, pero falla tan pronto como se sustituyen las variables: STR=$"Hello\nWorld"simplemente imprime Hello\nWorld.
ssc
3
@ssc comillas simples, no dobles
miken32
77
@ssc no hay variables en su ejemplo. Además, este método requiere comillas simples. Finalmente, para incluir variables interpoladas, tendría que concatenar cadenas entre comillas dobles y comillas simples especiales. por ejemplo, H="Hello"; W="World"; STR="${H}"$'\n'"${W}"; echo "${STR}"repetirá "Hola" y "Mundo" en líneas separadas
JDS
166

Echo es tan noventa y tan lleno de peligros que su uso debería dar como resultado volcados de núcleo no menos de 4 GB. En serio, los problemas de echo fueron la razón por la cual el proceso de estandarización de Unix finalmente inventó la printfutilidad, eliminando todos los problemas.

Entonces, para obtener una nueva línea en una cadena:

FOO="hello
world"
BAR=$(printf "hello\nworld\n") # Alternative; note: final newline is deleted
printf '<%s>\n' "$FOO"
printf '<%s>\n' "$BAR"

¡Ahí! Sin SYSV vs BSD echo madness, todo se imprime perfectamente y es totalmente portátil para secuencias de escape C. Todos, por favor, usen printfahora y nunca miren hacia atrás.

Jens
fuente
3
Gracias por esta respuesta, creo que proporciona muy buenos consejos. Sin embargo, tenga en cuenta que mi pregunta original no era realmente cómo obtener eco para imprimir nuevas líneas, sino cómo obtener una nueva línea en una variable de shell. Muestra cómo hacerlo también con printf, pero creo que la solución de usar anfetamáquinas$'string' es aún más limpia.
Juan A. Navarro
11
Solo para una definición deficiente de 'limpio'. La $'foo'sintaxis no es válida sintaxis POSIX a partir de 2013. Muchos shells se quejarán.
Jens
55
BAR=$(printf "hello\nworld\n")no imprime el final \ n para mí
Jonny
3
@ Jonny No debería; la especificación de shell para la sustitución de comandos dice que se eliminan una o más líneas nuevas al final de la salida. Me doy cuenta de que esto es inesperado para mi ejemplo y lo he editado para incluir una nueva línea en printf.
Jens
55
@ismael No, $()POSIX especifica que se eliminan las secuencias de uno o más caracteres <línea nueva> al final de la sustitución .
Jens
61

Lo que hice en base a las otras respuestas fue

NEWLINE=$'\n'
my_var="__between eggs and bacon__"
echo "spam${NEWLINE}eggs${my_var}bacon${NEWLINE}knight"

# which outputs:
spam
eggs__between eggs and bacon__bacon
knight
zvezda
fuente
30

El problema no es con el shell. El problema es en realidad con el echocomando en sí y la falta de comillas dobles alrededor de la interpolación variable. Puede intentar usar, echo -epero eso no es compatible con todas las plataformas, y una de las razones printfahora se recomienda para la portabilidad.

También puede intentar insertar la nueva línea directamente en su script de shell (si un script es lo que está escribiendo) para que se vea como ...

#!/bin/sh
echo "Hello
World"
#EOF

o equivalente

#!/bin/sh
string="Hello
World"
echo "$string"  # note double quotes!
Paso
fuente
24

Encuentro la -ebandera elegante y directa

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

Si la cadena es la salida de otro comando, solo uso comillas

indexes_diff=$(git diff index.yaml)
echo "$indexes_diff"
Simon Ndunda
fuente
2
Esto ya se ha mencionado en otras respuestas. Además, aunque ya estaba allí, acabo de editar la pregunta para enfatizar el hecho de que esta pregunta no se trata echo.
Juan A. Navarro
Esta no es una respuesta a la pregunta
Rohit Gupta
¡Esto responde totalmente a la pregunta que estaba buscando! ¡Gracias!
Kieveli
1
Funcionó bien para mí aquí. Solía ​​crear un archivo basado en este eco y generaba nuevas líneas en la salida correctamente.
Mateus Leon
echo -eEs conocido por sus problemas de portabilidad. Esta es una situación menos salvaje ahora que antes de POSIX, pero todavía no hay razón para preferir echo -e. Ver también stackoverflow.com/questions/11530203/…
tripleee el
21
  1. La única alternativa simple es escribir una nueva línea en la variable:

    $ STR='new
    line'
    $ printf '%s' "$STR"
    new
    line

    Sí, eso significa escribir Enterdonde sea necesario en el código.

  2. Hay varios equivalentes a un new linepersonaje.

    \n           ### A common way to represent a new line character.
    \012         ### Octal value of a new line character.
    \x0A         ### Hexadecimal value of a new line character.

    Pero todos ellos requieren "una interpretación" de alguna herramienta ( POSIX printf ):

    echo -e "new\nline"           ### on POSIX echo, `-e` is not required.
    printf 'new\nline'            ### Understood by POSIX printf.
    printf 'new\012line'          ### Valid in POSIX printf.
    printf 'new\x0Aline'       
    printf '%b' 'new\0012line'    ### Valid in POSIX printf.

    Y por lo tanto, la herramienta es necesaria para construir una cadena con una nueva línea:

    $ STR="$(printf 'new\nline')"
    $ printf '%s' "$STR"
    new
    line
  3. En algunos shells, la secuencia $'es una expansión especial de shell. Se sabe que funciona en ksh93, bash y zsh:

    $ STR=$'new\nline'
  4. Por supuesto, también son posibles soluciones más complejas:

    $ echo '6e65770a6c696e650a' | xxd -p -r
    new
    line

    O

    $ echo "new line" | sed 's/ \+/\n/g'
    new
    line

fuente
6

Un $ justo antes de las comillas simples '... \ n ...' como sigue, sin embargo, las comillas dobles no funcionan.

$ echo $'Hello\nWorld'
Hello
World
$ echo $"Hello\nWorld"
Hello\nWorld
caotín
fuente
4

No soy un experto en bash, pero este funcionó para mí:

STR1="Hello"
STR2="World"
NEWSTR=$(cat << EOF
$STR1

$STR2
EOF
)
echo "$NEWSTR"

Esto me resultó más fácil de formatear los textos.

Jason
fuente
2
Buen viejo heredoc . ¡Y probablemente también sea compatible con POSIX!
pyb
Las comillas $NEWSTRen echo "$NEWSTR"son importantes aquí
Shadi
0

En mi sistema (Ubuntu 17.10) su ejemplo simplemente funciona como lo desea, tanto cuando se escribe desde la línea de comando (en sh) como cuando se ejecuta como un shscript:

[bash sh
$ STR="Hello\nWorld"
$ echo $STR
Hello
World
$ exit
[bash echo "STR=\"Hello\nWorld\"
> echo \$STR" > test-str.sh
[bash cat test-str.sh 
STR="Hello\nWorld"
echo $STR
[bash sh test-str.sh 
Hello
World

Supongo que esto responde a tu pregunta: simplemente funciona. (No he tratado de descubrir detalles como en qué momento \nocurre exactamente la sustitución del carácter de nueva línea sh).

Sin embargo, me di cuenta de que esta misma secuencia de comandos se comportan de manera diferente cuando se ejecuta conbash y se imprima Hello\nWorlden su lugar:

[bash bash test-str.sh
Hello\nWorld

Logré obtener el resultado deseado de la bashsiguiente manera:

[bash STR="Hello
> World"
[bash echo "$STR"

Tenga en cuenta las comillas dobles alrededor $STR. Esto se comporta de manera idéntica si se guarda y ejecuta como un bashscript.

Lo siguiente también da el resultado deseado:

[bash echo "Hello
> World"
Alexey
fuente
0

Aquellos exigentes que solo necesitan la nueva línea y desprecian el código multilínea que rompe la sangría, podrían hacer:

IFS="$(printf '\nx')"
IFS="${IFS%x}"

Bash (y probablemente otros shells) engullen todas las nuevas líneas finales después de la sustitución del comando, por lo que debe finalizar la printfcadena con un carácter que no sea de nueva línea y eliminarla después. Esto también puede convertirse fácilmente en una línea.

IFS="$(printf '\nx')" IFS="${IFS%x}"

Sé que esto es dos acciones en lugar de una, pero mi sangría y portabilidad OCD está en paz ahora :) Originalmente desarrollé esto para poder dividir la salida separada solo de nueva línea y terminé usando una modificación que usa \rcomo el carácter de terminación. Eso hace que la división de nueva línea funcione incluso para la salida de dos que termina en \r\n.

IFS="$(printf '\n\r')"
tlwhitec
fuente
0

No estaba muy contento con ninguna de las opciones aquí. Esto es lo que funcionó para mí.

str=$(printf "%s" "first line")
str=$(printf "$str\n%s" "another line")
str=$(printf "$str\n%s" "and another line")
Adam K Dean
fuente
Esa es una forma muy tediosa de escribir str=$(printf '%s\n' "first line" "another line" "and another line")(el shell convenientemente (o no) recortará cualquier nueva línea final de la sustitución del comando). Varias respuestas aquí ya promueven printfen varias formas, así que no estoy seguro de que esto agregue algún valor como una respuesta separada.
tripleee
Es para cadenas cortas, pero si desea mantener las cosas ordenadas y cada línea tiene en realidad 60 caracteres, entonces es una forma ordenada de hacerlo. Dado que es la solución con la que finalmente fui, agrega algo, si no es para ti, para mí en el futuro cuando sin duda regrese.
Adam K Dean
1
Incluso para cadenas largas prefiero printf '%s\n' "first line" \ (nueva línea, sangría), 'second line'etc. Consulte también printf -v strpara asignar a una variable sin generar una subshell.
tripleee