469

This

STR="Hello\nWorld"
echo $STR

produces as output

Hello\nWorld

instead of

Hello
World

What should I do to have a newline in a string?

Note: This question is not about echo. I'm aware of echo -e, but I'm looking for a solution that allows passing a string (which includes a newline) as an argument to other commands that do not have a similar option to interpret \n's as newlines.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
Juan A. Navarro
  • 10,595
  • 6
  • 48
  • 52

13 Answers13

493

If you're using Bash, you can use backslash-escapes inside of a specially-quoted $'string'. For example, adding \n:

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

Prints:

Hello
World

If you're using pretty much any other shell, just insert the newline as-is in the string:

STR='Hello
World'

Bash recognizes a number of other backslash escape sequences in the $'' string. Here is an excerpt from the Bash manual page:

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.
amphetamachine
  • 27,620
  • 12
  • 60
  • 72
  • 86
    That's valid bash, but not POSIX sh. – jmanning2k Jun 06 '12 at 22:24
  • 9
    +1-just a note: export varDynamic="$varAnother1$varAnother2"$'\n' – Yordan Georgiev May 11 '13 at 08:19
  • on POSIX I would type `IFS='^J'` and use that. Any other option known? – Ostap Dec 01 '14 at 19:48
  • 6
    This does indeed work with literal strings as the OP asked, but fails as soon as variables are to be substituted: `STR=$"Hello\nWorld"` simply prints `Hello\nWorld`. – ssc Sep 26 '16 at 15:06
  • 4
    @ssc single quotes, not double – miken32 Mar 24 '17 at 16:16
  • 10
    @ssc there are no variables in your example. Also, this method requires single quotes. Finally, to include interpolated variables you would have to concatenate double-quoted and special-single-quoted strings together. e.g. `H="Hello"; W="World"; STR="${H}"$'\n'"${W}"; echo "${STR}"` will echo "Hello" and "World" on separate lines – JDS Nov 08 '17 at 16:07
  • @JDS (and miken32): Thanks for the extra explanation, much appreciated! Makes for ugly code, but works. :-) – ssc Dec 27 '17 at 22:53
  • how to have \n in sh? (not bash) – Dee Feb 11 '20 at 06:48
208

Echo is so nineties and so fraught with perils that its use should result in core dumps no less than 4GB. Seriously, echo's problems were the reason why the Unix Standardization process finally invented the printf utility, doing away with all the problems.

So to get a newline in a string, there are two ways:

# 1) Literal newline in an assignment.
FOO="hello
world"
# 2) Command substitution.
BAR=$(printf "hello\nworld\n") # Alternative; note: final newline is deleted
printf '<%s>\n' "$FOO"
printf '<%s>\n' "$BAR"

There! No SYSV vs BSD echo madness, everything gets neatly printed and fully portable support for C escape sequences. Everybody please use printf now for all your output needs and never look back.

Jens
  • 69,818
  • 15
  • 125
  • 179
  • 4
    Thanks for this answer, I think it provides really good advice. Note, however, that my original question wasn't really about how to get echo to print newlines, but how to get a newline into a shell variable. You show how to do that using printf as well, but I think that the solution from [amphetamachine](http://stackoverflow.com/users/237955/amphetamachine) using `$'string'` is even cleaner. – Juan A. Navarro May 16 '12 at 15:30
  • 14
    Only for a deficient definition of 'clean'. The `$'foo'` syntax is not valid POSIX syntax as of 2013. Many shells will complain. – Jens Feb 04 '13 at 13:23
  • 1
    Thanks for this answer. I get so screwed trying to use bash specific stuff in sh files all too often. – totowtwo Feb 10 '13 at 20:52
  • 5
    `BAR=$(printf "hello\nworld\n")` does not print the trailing \n for me – Jonny May 24 '13 at 03:47
  • 3
    @Jonny It shouldn't; the shell specification for command substitution says that one or more newlines at the end of the output are deleted. I realize this is unexpected for my example and have edited it to include a newline in the printf. – Jens May 24 '13 at 08:14
  • @Jens You show how to print a trailing newline after a variable, but how does one preserve the trailing newline WITHIN the variable? That is to say, I'd like to be able to say: `echo "$BAR" something` and have 'something' show up on a new line. – ishmael May 31 '14 at 21:17
  • @ishmael Then you need to make the newline part of the variable value, like I do with `FOO` (remove `world` to leave only `"` on that line.) – Jens Jun 01 '14 at 14:26
  • @Jens Thanks, but I was hoping for a way of doing it using '\n' and not a literal newline (which forces me to type two lines). Is there any way of making the $(...) evaluation retain the trailing newline? – ishmael Jun 02 '14 at 14:56
  • 6
    @ismael No, `$()` is specified by POSIX as **removing sequences of one or more characters at the end of the substitution**. – Jens Jun 03 '14 at 08:21
  • 2
    Well (in bash), try `printf -v var "%s\n\n" "valuea" "valueb"`. It seems to retain the last two `\n`. Clue: `echo -n "$var"|xxd`. :) –  Jul 28 '15 at 01:34
  • In sh: `var="$(printf "%s\n\n" "vala" "valb" "x")";echo -n "${var%x}" |xxd'` –  Jul 28 '15 at 01:43
  • 2
    That's: add an x at the end and then remove it. And redefine the printf to this new command. [Redefinition](http://wiki.bash-hackers.org/commands/builtin/printf) and the x trick from [Greg's Wiki](http://mywiki.wooledge.org/BashPitfalls#content.3D.24.28.3Cfile.29) point 41 of Bash Pitfails. –  Jul 28 '15 at 01:56
  • What are echo's problems really? – Qian Aug 24 '16 at 01:48
  • 1
    @TheoYou Echo's problems are that it is unspecified what it does when the first arg is -e, -n or -c and what happens when an arg ends in \c. BSD and SYSV echo behave differently. In a word: echo options are unportable, while printf is fully specified. – Jens Mar 24 '17 at 20:16
  • 1
    @TheoYou Predictably, there is a separate question with a good answer to that. This is literally my first Google hit for "why is printf preferred over echo": https://stackoverflow.com/questions/11530203/what-is-more-portable-echo-e-or-using-printf – tripleee Feb 14 '18 at 04:59
  • Thanks, now I can: `less "$(printf '+&^ INFO\nF')" /var/log/some*.log` – dlamblin Apr 06 '18 at 03:50
75

What I did based on the other answers was

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
  • 1,223
  • 9
  • 16
44

I find the -e flag elegant and straight forward

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

If the string is the output of another command, I just use quotes

indexes_diff=$(git diff index.yaml)
echo "$indexes_diff"
tripleee
  • 175,061
  • 34
  • 275
  • 318
Simon Ndunda
  • 894
  • 7
  • 9
  • 2
    This has already been mentioned in other answers. Also, although it was already there, I've just edited the question to stress the fact that this question is *not* about `echo`. – Juan A. Navarro Jun 05 '15 at 07:23
  • 1
    This is not an answer to the question – Rohit Gupta Jun 05 '15 at 07:39
  • This totally answers the question I was looking for! Thanks! – Kieveli Nov 17 '15 at 19:23
  • 1
    Worked well for me here. I've used to create a file based on this echo and it generated new lines on the output correctly. – Mateus Leon May 28 '16 at 04:19
  • 1
    `echo -e` is notorious for its portability issues. This is less of a Wild West situation now than pre-POSIX, but there is still no reason to prefer `echo -e`. See also https://stackoverflow.com/questions/11530203/what-is-more-portable-echo-e-or-using-printf – tripleee Mar 28 '19 at 17:04
35

The problem isn't with the shell. The problem is actually with the echo command itself, and the lack of double quotes around the variable interpolation. You can try using echo -e but that isn't supported on all platforms, and one of the reasons printf is now recommended for portability.

You can also try and insert the newline directly into your shell script (if a script is what you're writing) so it looks like...

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

or equivalently

#!/bin/sh
string="Hello
World"
echo "$string"  # note double quotes!
tripleee
  • 175,061
  • 34
  • 275
  • 318
Pace
  • 41,875
  • 13
  • 113
  • 156
  • Since this is more or less the canonical question for the non-[Bash question](https://stackoverflow.com/questions/8467424/) (and this is chronologically the first answer) and most importantly that the search engines like it, perhaps [add](https://stackoverflow.com/posts/3006137/edit) an example for printf? (But ***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today.) – Peter Mortensen Feb 26 '22 at 17:12
22
  1. The only simple alternative is to actually type a new line in the variable:

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

Yes, that means writing Enter where needed in the code.

  1. There are several equivalents to a new line character.

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

But all those require "an interpretation" by some tool (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.

And therefore, the tool is required to build a string with a new-line:

    $ STR="$(printf 'new\nline')"
    $ printf '%s' "$STR"
    new
    line
  1. In some shells, the sequence $' is a special shell expansion. Known to work in ksh93, bash and zsh:

    $ STR=$'new\nline'
    
  2. Of course, more complex solutions are also possible:

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

Or

    $ echo "new line" | sed 's/ \+/\n/g'
    new
    line
Jens
  • 69,818
  • 15
  • 125
  • 179
9

A $ right before single quotation marks '...\n...' as follows, however double quotation marks doesn't work.

$ echo $'Hello\nWorld'
Hello
World
$ echo $"Hello\nWorld"
Hello\nWorld
caot
  • 3,066
  • 35
  • 37
4

Disclaimer: I first wrote this and then stumbled upon this question. I thought this solution wasn't yet posted, and saw that tlwhitec did post a similar answer. Still I'm posting this because I hope it's a useful and thorough explanation.

Short answer:

This seems quite a portable solution, as it works on quite some shells (see comment).
This way you can get a real newline into a variable.

The benefit of this solution is that you don't have to use newlines in your source code, so you can indent your code any way you want, and the solution still works. This makes it robust. It's also portable.

# Robust way to put a real newline in a variable (bash, dash, ksh, zsh; indentation-resistant).
nl="$(printf '\nq')"
nl=${nl%q}

Longer answer:

Explanation of the above solution:

The newline would normally be lost due to command substitution, but to prevent that, we add a 'q' and remove it afterwards. (The reason for the double quotes is explained further below.)

We can prove that the variable contains an actual newline character (0x0A):

printf '%s' "$nl" | hexdump -C
00000000  0a  |.|
00000001

(Note that the '%s' was needed, otherwise printf will translate a literal '\n' string into an actual 0x0A character, meaning we would prove nothing.)

Of course, instead of the solution proposed in this answer, one could use this as well (but...):

nl='
'

... but that's less robust and can be easily damaged by accidentally indenting the code, or by forgetting to outdent it afterwards, which makes it inconvenient to use in (indented) functions, whereas the earlier solution is robust.

Now, as for the double quotes:
The reason for the double quotes " surrounding the command substitution as in nl="$(printf '\nq')" is that you can then even prefix the variable assignment with the local keyword or builtin (such as in functions), and it will still work on all shells, whereas otherwise the dash shell would have trouble, in the sense that dash would otherwise lose the 'q' and you'd end up with an empty 'nl' variable (again, due to command substitution).
That issue is better illustrated with another example:

dash_trouble_example() {
    e=$(echo hello world) # Not using 'local'.
    echo "$e" # Fine. Outputs 'hello world' in all shells.

    local e=$(echo hello world) # But now, when using 'local' without double quotes ...:
    echo "$e" # ... oops, outputs just 'hello' in dash,
              # ... but 'hello world' in bash and zsh.

    local f="$(echo hello world)" # Finally, using 'local' and surrounding with double quotes.
    echo "$f" # Solved. Outputs 'hello world' in dash, zsh, and bash.

    # So back to our newline example, if we want to use 'local', we need
    # double quotes to surround the command substitution:
    # (If we didn't use double quotes here, then in dash the 'nl' variable
    # would be empty.)
    local nl="$(printf '\nq')"
    nl=${nl%q}
}

Practical example of the above solution:

# Parsing lines in a for loop by setting IFS to a real newline character:

nl="$(printf '\nq')"
nl=${nl%q}

IFS=$nl

for i in $(printf '%b' 'this is line 1\nthis is line 2'); do
    echo "i=$i"
done

# Desired output:
# i=this is line 1
# i=this is line 2

# Exercise:
# Try running this example without the IFS=$nl assignment, and predict the outcome.
Jens
  • 69,818
  • 15
  • 125
  • 179
Jelle Geerts
  • 833
  • 8
  • 12
3

I'm no bash expert, but this one worked for me:

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

$STR2
EOF
)
echo "$NEWSTR"

I found this easier to formatting the texts.

Jason
  • 2,950
  • 2
  • 30
  • 50
  • 2
    Good old [heredoc](https://en.wikipedia.org/wiki/Here_document#Unix_shells). And it's probably POSIX compliant too! – pyb Aug 24 '17 at 15:48
  • 1
    The quotes around `$NEWSTR` in `echo "$NEWSTR"` are important here – Shadi Aug 15 '19 at 06:30
  • 1
    @Shadi, thanks! I made many attempts in other solutions to output string, but it didn't split into lines, until I saw your comment. – CoolMind Mar 01 '23 at 11:53
1

Those picky ones that need just the newline and despise the multiline code that breaks indentation, could do:

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

Bash (and likely other shells) gobble all the trailing newlines after command substitution, so you need to end the printf string with a non-newline character and delete it afterwards. This can also easily become a oneliner.

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

I know this is two actions instead of one, but my indentation and portability OCD is at peace now :) I originally developed this to be able to split newline-only separated output and I ended up using a modification that uses \r as the terminating character. That makes the newline splitting work even for the dos output ending with \r\n.

IFS="$(printf '\n\r')"
tlwhitec
  • 1,845
  • 16
  • 15
1

This isn't ideal, but I had written a lot of code and defined strings in a way similar to the method used in the question. The accepted solution required me to refactor a lot of the code so instead, I replaced every \n with "$'\n'" and this worked for me.

0

On my system (Ubuntu 17.10) your example just works as desired, both when typed from the command line (into sh) and when executed as a sh script:

[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

I guess this answers your question: it just works. (I have not tried to figure out details such as at what moment exactly the substitution of the newline character for \n happens in sh).

However, i noticed that this same script would behave differently when executed with bash and would print out Hello\nWorld instead:

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

I've managed to get the desired output with bash as follows:

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

Note the double quotes around $STR. This behaves identically if saved and run as a bash script.

The following also gives the desired output:

[bash]§ echo "Hello
> World"
Alexey
  • 3,843
  • 6
  • 30
  • 44
0

I wasn't really happy with any of the options here. This is what worked for me.

str=$(printf "%s" "first line")
str=$(printf "$str\n%s" "another line")
str=$(printf "$str\n%s" "and another line")
Adam K Dean
  • 7,387
  • 10
  • 47
  • 68
  • That's a very tedious way to write `str=$(printf '%s\n' "first line" "another line" "and another line")` (the shell will conveniently (or not) trim any final newlines from the command substitution). Several answers here already promote `printf` in various forms so I'm not sure this adds any value as a separate answer. – tripleee Mar 25 '19 at 16:50
  • It is for short strings, but if you want to keep things tidy and each line is actually 60 characters long, then it's a tidy way of doing it. Given that it is the solution I eventually went with, it does add something, if not for you, then for myself in the future when I undoubtedly return. – Adam K Dean Mar 28 '19 at 16:39
  • 2
    Even for long strings I prefer `printf '%s\n' "first line" \ ` (newline, indent) `'second line'` etc. See also `printf -v str` for assigning to a variable without spawning a subshell. – tripleee Mar 28 '19 at 17:08