Come posso avere una nuova riga in una stringa in sh?

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 eco . Sono a conoscenza di echo -e , ma sto cercando una soluzione che permetta di passare una stringa (che include una nuova riga) come argomento ad altri comandi che non hanno un’opzione simile per interpretare \n come linee nuove.

La soluzione è usare $'string' , per esempio:

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

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

L’eco è così novanta e così pieno di pericoli che il suo uso dovrebbe comportare scarti di base non inferiori a 4 GB. Seriamente, i problemi di eco sono stati il ​​motivo per cui il processo di standardizzazione di Unix ha finalmente inventato l’utility printf , 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à! No SYSV vs BSD echo follia, tutto viene stampato in modo preciso e supporto completamente portatile per le sequenze di escape C. Per favore, tutti usano printf ora e non guardano mai indietro.

Quello che ho fatto in base alle 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 

Il problema non è con la shell. Il problema è in realtà con il comando echo stesso e la mancanza di virgolette sull’interpolazione delle variabili. Puoi provare a usare echo -e ma non è supportato su tutte le piattaforms e uno dei motivi per cui printf è ora raccomandato per la portabilità.

Puoi anche provare e inserire la nuova riga direttamente nello script della shell (se uno script è ciò che stai scrivendo), quindi sembra che …

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

o equivalentemente

 #!/bin/sh string="Hello World" echo "$string" # note double quotes! 
  1. L’unica alternativa semplice è in realtà digitare una nuova riga nella variabile:

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

    Sì, ciò significa scrivere Invio dove necessario nel codice.

  2. Ci sono diversi equivalenti a un new line carattere di 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. 

    Ma tutti richiedono “un’interpretazione” con qualche strumento ( 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. 

    E quindi, lo strumento è necessario per build 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 

Trovo la bandiera -e elegante e diretta

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

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

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

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

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

Ho trovato più semplice la formattazione dei testi.

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

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

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

Immagino che questo risponda alla tua domanda: funziona e basta. (Non ho cercato di capire dettagli come in che momento esattamente la sostituzione del carattere di nuova riga per \n accade in sh ).

Tuttavia, ho notato che questo stesso script si sarebbe comportato diversamente quando è stato eseguito con bash e invece avrebbe stampato Hello\nWorld :

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

Sono riuscito a ottenere l’output desiderato con bash come segue:

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

Notare le doppie virgolette attorno a $STR . Questo si comporta in modo identico se salvato ed eseguito come uno script bash .

Quanto segue fornisce anche l’output desiderato:

 [bash]§ echo "Hello > World" 

Quelli 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 newline finali dopo la sostituzione del comando, quindi è necessario terminare la stringa printf con un carattere non di nuova riga ed eliminarlo in seguito. Questo può anche facilmente diventare un oneliner.

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

So che questo è due azioni invece di una, ma la mia indentazione e portabilità OCD è in pace ora 🙂 Inizialmente ho sviluppato questo per essere in grado di dividere l’output separato solo di nuova riga e ho finito per usare una modifica che utilizza \r come terminazione carattere. Ciò rende il lavoro di divisione newline anche per l’output dos che termina con \r\n .

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