Come posso avere una newline in una stringa in sh?


339

Questo

STR="Hello\nWorld"
echo $STR

produce come output

Hello\nWorld

invece di

Hello
World

Cosa devo fare per avere una nuova riga in una stringa?

Nota: questa domanda non riguarda l' eco . Sono a conoscenza echo -e, ma sto cercando una soluzione che consenta di passare una stringa (che include una nuova riga) come argomento ad altri comandi che non hanno un'opzione simile per interpretare quelli \ncome nuove righe.

Risposte:


353

La soluzione è utilizzare $'string', ad esempio:

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

Ecco un estratto dalla pagina del manuale di 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.

74
Questo è valido bash, ma non POSIX sh.
jmanning2k,

7
+ 1-solo una nota: export varDynamic = "$ varAnother1 $ varAnother2" $ '\ n'
Yordan Georgiev,

3
Questo funziona davvero con stringhe letterali come richiesto dall'OP, ma fallisce non appena le variabili devono essere sostituite: STR=$"Hello\nWorld"semplicemente stampa Hello\nWorld.
ssc,

3
@ssc virgolette singole, non doppie
miken32

7
@ssc non ci sono variabili nel tuo esempio. Inoltre, questo metodo richiede virgolette singole. Infine, per includere variabili interpolate dovresti concatenare insieme stringhe tra virgolette doppie e virgolette speciali singole. ad esempio H="Hello"; W="World"; STR="${H}"$'\n'"${W}"; echo "${STR}", echeggerà "Hello" e "World" su righe separate
JDS

166

L'eco è così anni novanta e così pieno di pericoli che il suo uso dovrebbe tradursi in dump core non inferiori a 4 GB. Seriamente, i problemi di eco sono stati il ​​motivo per cui il processo di standardizzazione Unix ha finalmente inventato l' printfutilità, eliminando tutti i problemi.

Quindi per ottenere una nuova riga in una stringa:

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

Là! Nessuna follia di eco tra SYSV e BSD, tutto viene stampato in modo ordinato e supporto completamente portatile per le sequenze di escape C. Tutti, per favore, usa printfora e non guardare mai indietro.


3
Grazie per questa risposta, penso che fornisca davvero buoni consigli. Si noti, tuttavia, che la mia domanda originale non riguardava davvero come ottenere l'eco per stampare nuove righe, ma come ottenere una riga nuova in una variabile shell. Mostrate come farlo usando anche printf, ma penso che la soluzione dell'uso di anfetamachine$'string' sia ancora più pulita.
Juan A. Navarro,

11
Solo per una definizione carente di "pulito". La $'foo'sintassi non è sintassi POSIX valida a partire dal 2013. Molte shell si lamenteranno.
Jens,

5
BAR=$(printf "hello\nworld\n")non stampa il finale \ n per me
Jonny

3
@Jonny Non dovrebbe; le specifiche della shell per la sostituzione dei comandi indicano che una o più nuove righe alla fine dell'output vengono eliminate. Mi rendo conto che questo è inaspettato per il mio esempio e l'ho modificato per includere una nuova riga in printf.
Jens,

5
@ismael No, $()è specificato da POSIX come rimozione di sequenze di uno o più caratteri <newline> alla fine della sostituzione .
Jens,

61

Quello che ho fatto sulla base delle altre risposte è stato

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

30

Il problema non è con la shell. Il problema è in realtà con il echocomando stesso e la mancanza di virgolette doppie attorno all'interpolazione variabile. Puoi provare a utilizzare echo -ema questo non è supportato su tutte le piattaforme e uno dei motivi printfè ora consigliato per la portabilità.

Puoi anche provare a inserire la nuova riga direttamente nello script della shell (se uno script è quello che stai scrivendo) in modo che appaia ...

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

o equivalentemente

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

24

Trovo la -ebandiera elegante e semplice

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

Se la stringa è l'output di un altro comando, uso solo virgolette

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

2
Questo è già stato menzionato in altre risposte. Inoltre, sebbene fosse già lì, ho appena modificato la domanda per sottolineare il fatto che questa domanda non riguarda echo.
Juan A. Navarro,

Questa non è una risposta alla domanda
Rohit Gupta,

Questo risponde totalmente alla domanda che stavo cercando! Grazie!
Kieveli,

1
Ha funzionato bene per me qui. Ho usato per creare un file basato su questo eco e ha generato correttamente nuove righe sull'output.
Mateus Leon,

echo -eè noto per i suoi problemi di portabilità. Questa è una situazione meno selvaggia del West che ora pre-POSIX, ma non c'è ancora motivo di preferire echo -e. Vedere anche stackoverflow.com/questions/11530203/...
tripleee

21
  1. L'unica semplice alternativa è in realtà digitare una nuova riga nella variabile:

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

    Sì, questo significa scrivere Enterdove necessario nel codice.

  2. Esistono diversi equivalenti a un new linepersonaggio.

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

    Ma tutti richiedono "un'interpretazione" da parte di alcuni strumenti ( 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.

    Pertanto, lo strumento è necessario per creare una stringa con una nuova riga:

    $ STR="$(printf 'new\nline')"
    $ printf '%s' "$STR"
    new
    line
  3. In alcune shell, la sequenza $'è un'espansione speciale della shell. Conosciuto per funzionare in ksh93, bash e zsh:

    $ STR=$'new\nline'
  4. Naturalmente, sono anche possibili soluzioni più complesse:

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

    O

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

6

Un $ prima delle virgolette singole '... \ n ...' come segue, tuttavia le doppie virgolette non funzionano.

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

4

Non sono un esperto di bash, ma questo ha funzionato per me:

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

$STR2
EOF
)
echo "$NEWSTR"

Ho trovato più facile la formattazione dei testi.


2
Buona vecchia eredità . Ed è probabilmente compatibile anche con POSIX!
pyb,

Le virgolette intorno $NEWSTRa echo "$NEWSTR"sono importanti qui
Shadi

0

Sul mio sistema (Ubuntu 17.10) il tuo esempio funziona come desiderato, sia quando digitato dalla riga di comando (in sh) che quando eseguito come uno 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

Immagino che questo risponda alla tua domanda: funziona e basta. (Non ho provato a capire dettagli come in quale momento \navviene esattamente la sostituzione del personaggio newline sh).

Tuttavia, ho notato che questo stesso script si sarebbe comportato diversamente quando eseguito conbash e avrebbe stampato Hello\nWorldinvece:

[bash bash test-str.sh
Hello\nWorld

Sono riuscito a ottenere l'output desiderato con bashil seguente:

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

Nota le doppie virgolette in giro $STR. Questo si comporta in modo identico se salvato ed eseguito come bashscript.

Quanto segue fornisce anche l'output desiderato:

[bash echo "Hello
> World"

0

Quelle esigenti che hanno bisogno solo della nuova riga e disprezzano il codice multilinea che rompe il rientro, potrebbero fare:

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

Bash (e probabilmente altre shell) divorano tutte le nuove righe finali dopo la sostituzione del comando, quindi è necessario terminare la printfstringa con un carattere non newline ed eliminarla in seguito. Questo può anche facilmente diventare un oneliner.

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

So che si tratta di due azioni invece di una, ma ora il mio rientro e portabilità OCD sono in pace :) Ho originariamente sviluppato questo per essere in grado di dividere l'output separato solo per la nuova riga e ho finito per usare una modifica che usa \rcome carattere finale. Questo fa sì che la divisione della nuova riga funzioni anche per l'output dos che termina con \r\n.

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

0

Non ero davvero contento di nessuna delle opzioni qui. Questo è ciò che ha funzionato per me.

str=$(printf "%s" "first line")
str=$(printf "$str\n%s" "another line")
str=$(printf "$str\n%s" "and another line")

Questo è un modo molto noioso per scrivere str=$(printf '%s\n' "first line" "another line" "and another line")(la shell taglierà convenientemente (o meno) qualsiasi nuova riga finale dalla sostituzione del comando). Diverse risposte qui promuovono già printfin varie forme, quindi non sono sicuro che ciò aggiunga alcun valore come risposta separata.
triplo

È per stringhe brevi, ma se vuoi mantenere le cose in ordine e ogni riga è in realtà lunga 60 caratteri, allora è un modo ordinato per farlo. Dato che è la soluzione che alla fine ho scelto, aggiunge qualcosa, se non per te, quindi per me stesso in futuro, quando indubbiamente tornerò.
Adam K Dean,

1
Preferisco anche per stringhe lunghe printf '%s\n' "first line" \ (newline, indent) 'second line'ecc. Vedi anche printf -v strper l'assegnazione a una variabile senza spawn di una subshell.
Tripleee,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.