117

When I do

str="Hello World\n===========\n"

I get the \n printed out too. How can I have newlines then?

2

9 Answers 9

132

In bash you can use the syntax

str=$'Hello World\n===========\n'

Single quotes preceded by a $ is a new syntax that allows to insert escape sequences in strings.

Also printf builtin allows to save the resulting output to a variable

printf -v str 'Hello World\n===========\n'

Both solutions do not require a subshell.

If in the following you need to print the string, you should use double quotes, like in the following example:

echo "$str"

because when you print the string without quotes, newline are converted to spaces.

4
  • 2
    What is the syntax str=$'Hello World\n===========\n' called? variable substitution?
    – zengr
    Aug 10, 2013 at 0:28
  • 10
    @zengr: It's called ANSI-C quoting, and it's also supported in zsh and ksh; however, it is NOT POSIX-compliant.
    – mklement0
    Jul 8, 2014 at 14:46
  • 6
    @mkelement0, it comes from ksh93, is also supported by zsh, bash, mksh and FreeBSD sh, and its inclusion in the next major revision of POSIX is under discussion Sep 26, 2016 at 9:42
  • 3
    Doesn't seem to work with double quotes? e.g. str=$"My $PET eats:\n$PET food" ? This approach works for double quotes
    – Brad Parks
    Sep 26, 2019 at 12:33
39

You can put literal newlines within single quotes (in any Bourne/POSIX-style shell).

str='Hello World
===========
'

For a multiline string, here documents are often convenient. The string is fed as input to a command.

mycommand <<'EOF'
Hello World
===========
EOF

If you want to store the string in a variable, use the cat command in a command substitution. The newline character(s) at the end of the string will be stripped by the command substitution. If you want to retain the final newlines, put a stopper at the end and strip it away afterward. In POSIX-compliant shells, you can write str=$(cat <<'EOF'); str=${str%a} followed by the heredoc proper, but bash requires the heredoc to appear before the closing parenthesis.

str=$(cat <<'EOF'
Hello World
===========
a
EOF
); str=${str%a}

In ksh, bash and zsh, you can use the $'…' quoted form to expand backslash escapes inside the quotes.

str=$'Hello World\n===========\n'
6
  • 1
    I'm using GNU bash 4.1.5, and str=$(cat <<'EOF') doesn't work as-is.. The ) needs to be placed on the next line after the end-of-doc EOF.. but even so, it loses the trailing newline due to Command Substitution.
    – Peter.O
    Sep 3, 2011 at 16:49
  • @fred Good points, I've explained about the trailing newlines and shown code that works in bash. I think it's a bug in bash, though to be honest upon rereading the POSIX spec I find it unclear that the behavior is mandated when the << is inside a command substitution and the heredoc isn't. Sep 4, 2011 at 17:01
  • Great stuff; to preserve trailing (and leading) \n instances in bash when capturing a here-doc in a variable, consider IFS= read -r -d '' str <<'EOF'... as an alternative to the stopper approach (see my answer).
    – mklement0
    Jul 8, 2014 at 15:29
  • The very first example doesn't work in my bash. It results in str=Hello World
    – mblakesley
    Sep 15, 2020 at 20:37
  • 1
    @mblakesley It does work. You've made a mistake somewhere. Sep 15, 2020 at 20:48
20

If you need newlines in your script many times you could declare a global variable holding a newline. That way you can use it in double-quoted strings (variable expansions).

NL=$'\n'
str="Hello World${NL} and here is a variable $PATH ===========${NL}"
6
  • Why would $'' require a subshell?
    – Mat
    Jun 10, 2013 at 7:43
  • Sorry, I misread an answer.
    – pihentagy
    Jun 10, 2013 at 11:19
  • Could I ask for explanation from downvoters?
    – pihentagy
    Jan 23, 2017 at 10:12
  • Nice! This can be included in variables using double quotes, e.g. "My dog eats:${NL}dog food"
    – Brad Parks
    Sep 26, 2019 at 12:30
  • This worked for me. When using a bash script to composite a string that gets automatically copied onto the clipboard for subsequent pasting elsewhere, this approach worked for adding newlines in the resulting output.
    – Ville
    Oct 28, 2019 at 22:36
12

Are you using "echo"? Try "echo -e".

echo -e "Hello World\n===========\n"
4
  • 2
    BTW. You don't need the final \n, because echo will automatically add one, unless you specify -n. (However, the main brunt of the question, is how to get these newlines into a variable).
    – Peter.O
    Sep 3, 2011 at 8:48
  • +1 Of all the solutions, this one is the most direct and simplest.
    – Hai Vu
    Sep 3, 2011 at 16:11
  • echo -e doesn't work in OS X Feb 4, 2014 at 20:21
  • 2
    @GregKrsak: In bash, echo -e does work on OS X - that's because echo is a bash builtin (rather than an external executable) and that builtin does support -e. (As a builtin, it should work on all platforms that bash runs on; incidentally, echo -e works in ksh and zsh too). By contrast, however, the external echo utility on OS X - /bin/echo - indeed does not support -e.
    – mklement0
    Jul 8, 2014 at 14:54
7

To complement the great existing answers:

If you're using bash and you prefer using actual newlines for readability, read is another option for capturing a here-doc in a variable, which (like other solutions here) doesn't require use of a subshell.

# Reads a here-doc, trimming leading and trailing whitespace.
# Use `IFS= read ...` to preserve it (the trailing \n, here).
read -r -d '' str <<'EOF'   # Use `IFS= read ...` to preserve the trailing \n
Hello World
===========
EOF
# Test: output the variable enclosed in "[...]", to show the value's boundaries.
$ echo "$str"
[Hello World
===========]
  • -r ensures that read doesn't interpret the input (by default, it would treat backslashes special, but that is rarely needed).

  • -d '' sets the "record" delimiter to an empty string, causing read to read the entire input at once (instead of just a single line).

Note that by leaving $IFS (the internal field separator) at its default, $' \t\n' (a space, a tab, a newline), any leading and trailing whitespace is trimmed from the value assigned to $str, which includes the here-doc's trailing newline.
(Note that even though the here-doc's body starts on the line after the start delimiter ('EOF' here), it does not contain a leading newline).

Usually, this is the desired behavior, but if you do want that trailing newline, use IFS= read -r -d '' instead of just read -r -d '', but note that any leading and trailing whitespace is then preserved.
(Note that prepending IFS=  directly to the read command means that the assignment is in effect during that command only, so there is no need to restore the previous value.)


Using a here-doc also allows you to optionally use indentation to set off the multiline string for readability:

# Caveat: indentation must be actual *tab* characters - spaces won't work.
read -r -d '' str <<-'EOF' # NOTE: Only works if the indentation uses actual tab (\t) chars.
    Hello World
    ===========
EOF
# Output the variable enclosed in "[...]", to show the value's boundaries.
# Note how the leading tabs were stripped.
$ echo "$str"
[Hello World
===========]

Placing - between << and the opening here-doc delimiter ('EOF', here) causes leading tab characters to be stripped from the here-doc body and even the closing delimiter, but do note that this only works with actual tab characters, not spaces, so if your editor translates tab keypresses into spaces, extra work is needed.

6

From all discussion, here is the simplest way for me:

bash$ str="Hello World
==========="
bash$ echo "$str"
Hello World
===========

The echo command must use double quotes.

4
#!/bin/bash

result=""

foo="FOO"
bar="BAR"

result+=$(printf '%s' "$foo")$'\n'
result+=$(printf '%s' "$bar")$'\n'

echo "$result"
printf '%s' "$result"

output:

FOO
BAR

FOO
BAR
3
  • 3
    Why not simply result+=$foo$'\n'? $(printf %s "$foo") would trim the trailing newline characters in $foo if any. Sep 26, 2016 at 9:50
  • 1
    There is no explanation for the code, else it looks fine to me. Jul 17, 2019 at 5:54
  • This makes sense when the printf format is more complex than just '%s'. It's the best way I've seen so far to solve my problem. Sep 2, 2020 at 0:09
0

The first comment on the question mentions arrays, but nobody shows how to do it with arrays, so here it is.

str=( "Hello World" )
str+=(===========)
printf '%s\n' "${str[@]}"

The first line creates an array with one element. Here the quotes are necessary (single quotes work too), otherwise the two words would be added as two separate array elements.

The second line adds one more element to the array with the += operator. Here the quotes are optional, because there is no whitespace.

The third line prints out each array element as string (%s) followed by a newline (\n).

There can be whitespace around the array elements, on the first line I added spaces between the string and brackets, on the second I omitted spaces, to show it works both ways.

-2

you need to do it this way:

STR=$(echo -ne "Hello World\n===========\n")

Update:

As Fred pointed it out, this way you will loose trailing "\n". To assign variable with backslash sequences expanded, do:

STR=$'Hello World\n===========\n\n'

let's test it:

echo "[[$STR]]"

gives us now:

[[Hello World
===========

]]

Note, that $'' is different than $"". The second does translation according to current locale. For deital see QUOTING section in man bash.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.