web-dev-qa-db-fra.com

Comment puis-je avoir une nouvelle ligne dans une chaîne dans sh?

Ce

STR="Hello\nWorld"
echo $STR

produit en sortie

Hello\nWorld

au lieu de

Hello
World

Que dois-je faire pour avoir une nouvelle ligne dans une chaîne?

Note: Cette question ne concerne pas echo. Je connais echo -e, mais je cherche une solution qui permette de passer une chaîne (qui inclut une nouvelle ligne) en tant qu'argument pour autres commandes ne disposant pas d'une option similaire à interpréter. \n en nouvelles lignes.

250
Juan A. Navarro

La solution consiste à utiliser $'string', par exemple:

$ STR=$'Hello\nWorld'
$ echo "$STR"
Hello
World

Voici un extrait de la page de manuel 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.
268
amphetamachine

Echo a tellement de vie et de périls que son utilisation ne devrait pas générer moins de 4 Go de mémoire. Sérieusement, les problèmes d’écho étaient la raison pour laquelle le processus de normalisation Unix avait finalement inventé l’utilitaire printf, supprimant tous les problèmes.

Donc, pour obtenir une nouvelle ligne dans une chaîne:

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

Là! Aucun SYSV vs BSD ne fait écho à la folie, tout est clairement imprimé et supporte de manière entièrement portable les séquences d'échappement en C. Tout le monde s'il vous plaît utilisez printf maintenant et ne jamais regarder en arrière.

135
Jens

Ce que j'ai fait sur la base des autres réponses était

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
47
zvezda

Le problème n'est pas avec le shell. Le problème vient en réalité de la commande echo et de l'absence de guillemets autour de l'interpolation de variable. Vous pouvez essayer d'utiliser echo -e mais cela n'est pas pris en charge sur toutes les plateformes et l'une des raisons pour lesquelles printf est maintenant recommandée pour la portabilité.

Vous pouvez également essayer d'insérer la nouvelle ligne directement dans votre script Shell (si un script est ce que vous écrivez) afin qu'il ressemble ...

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

ou équivalent

#!/bin/sh
string="Hello
World"
echo "$string"  # note double quotes!
24
Pace
  1. La seule alternative simple est de taper une nouvelle ligne dans la variable:

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

    Oui, cela signifie écrire Enter si nécessaire dans le code.

  2. Il existe plusieurs équivalents à un caractère new line.

    \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.
    

    Mais tous ceux qui nécessitent "une interprétation" par un outil ( 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.
    

    Et par conséquent, l'outil est nécessaire pour construire une chaîne avec une nouvelle ligne:

    $ STR="$(printf 'new\nline')"
    $ printf '%s' "$STR"
    new
    line
    
  3. Dans certains coquillages, la séquence $' est une extension spéciale de Shell. Connu pour fonctionner en ksh93, bash et zsh:

    $ STR=$'new\nline'
    
  4. Bien entendu, des solutions plus complexes sont également possibles:

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

    Ou

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

Je trouve le drapeau -e élégant et simple

STR="Hello\nWorld" echo -e $STR # output Hello World

Si la chaîne est la sortie d'une autre commande, j'utilise simplement des guillemets

indexes_diff=$(git diff index.yaml) echo "$indexes_diff"

8
Simon Ndunda

Un $ juste avant les guillemets simples '...\n ...' comme suit, mais les guillemets doubles ne fonctionnent pas.

$ echo $'Hello\nWorld'
Hello
World
$ echo $"Hello\nWorld"
Hello\nWorld
4
caot

Je ne suis pas un expert en bash, mais celui-ci a fonctionné pour moi:

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

$STR2
EOF
)
echo "$NEWSTR"

J'ai trouvé cela plus facile de formater les textes.

3
Jason

Les plus difficiles qui ont juste besoin de la nouvelle ligne et méprisent le code multiligne qui rompt l’indentation pourraient faire:

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

Bash (et probablement d'autres coquilles) engloutit tous les retours à la ligne après le remplacement de la commande. Vous devez donc terminer la chaîne printf par un caractère autre que de nouvelle ligne, puis le supprimer. Cela peut aussi facilement devenir un oneliner.

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

Je sais que c’est deux actions au lieu d’une, mais mon indentation et ma portabilité OCD sont en paix à présent :) j’ai développé ce système pour pouvoir séparer les sorties séparées entre nouvelles lignes et j’ai finalement utilisé une modification qui utilise \r comme caractère final . Cela permet au fractionnement de la nouvelle ligne de fonctionner même pour la sortie dos se terminant par \r\n.

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

Je n'étais vraiment pas content des options ici. C'est ce qui a fonctionné pour moi.

str=$(printf "%s" "first line")
str=$(printf "$str\n%s" "another line")
str=$(printf "$str\n%s" "and another line")
0
Adam K Dean

Sur mon système (Ubuntu 17.10), votre exemple fonctionne comme vous le souhaitez, à la fois lorsque vous le saisissez à partir de la ligne de commande (dans sh) et lorsqu'il est exécuté en tant que script sh:

[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

Je suppose que cela répond à votre question: cela fonctionne. (Je n'ai pas essayé de comprendre des détails tels qu'à quel moment exactement la substitution du caractère de nouvelle ligne pour \n se produit dans sh).

Cependant, j’ai remarqué que ce même script se comporterait différemment lorsqu’il serait exécuté avec bashet afficherait Hello\nWorld à la place:

[bash]§ bash test-str.sh
Hello\nWorld

J'ai réussi à obtenir la sortie souhaitée avec bash comme suit:

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

Notez les guillemets autour de $STR. Cela se comporte de manière identique s’il est enregistré et exécuté en tant que script bash.

Ce qui suit donne également la sortie souhaitée:

[bash]§ echo "Hello
> World"
0
Alexey