148

I have

var="a b c"
for i in $var
do
   p=`echo -e $p'\n'$i`
done
echo $p

I want the last echo to print:

a
b
c

Notice that I want the variable p to contain newlines. How do I do that?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ankur Agarwal
  • 23,692
  • 41
  • 137
  • 208

7 Answers7

245

Summary

  1. Inserting a new line in the source code

     p="${var1}
     ${var2}"
     echo "${p}"
    
  2. Using $'\n' (only Bash and Z shell)

     p="${var1}"$'\n'"${var2}"
     echo "${p}"
    
  3. Using echo -e to convert \n to a new line

     p="${var1}\n${var2}"
     echo -e "${p}"
    

Details

  1. Inserting a new line in the source code

     var="a b c"
     for i in $var
     do
        p="$p
     $i"       # New line directly in the source code
     done
     echo "$p" # Double quotes required
               # But -e not required
    

    Avoid extra leading newline

     var="a b c"
     first_loop=1
     for i in $var
     do
        (( $first_loop )) &&  # "((...))" is Bash specific
        p="$i"            ||  # First -> Set
        p="$p
     $i"                      # After -> Append
        unset first_loop
     done
     echo "$p"                # No need -e
    

    Using a function

     embed_newline()
     {
        local p="$1"
        shift
        for i in "$@"
        do
           p="$p
     $i"                      # Append
        done
        echo "$p"             # No need -e
     }
    
     var="a b c"
     p=$( embed_newline $var )  # Do not use double quotes "$var"
     echo "$p"
    
  2. Using $'\n' (less portable)

    and interprets $'\n' as a new line.

     var="a b c"
     for i in $var
     do
        p="$p"$'\n'"$i"
     done
     echo "$p" # Double quotes required
               # But -e not required
    

    Avoid extra leading newline

     var="a b c"
     first_loop=1
     for i in $var
     do
        (( $first_loop )) &&  # "((...))" is bash specific
        p="$i"            ||  # First -> Set
        p="$p"$'\n'"$i"       # After -> Append
        unset first_loop
     done
     echo "$p"                # No need -e
    

    Using a function

     embed_newline()
     {
        local p="$1"
        shift
        for i in "$@"
        do
           p="$p"$'\n'"$i"    # Append
        done
        echo "$p"             # No need -e
     }
    
     var="a b c"
     p=$( embed_newline $var )  # Do not use double quotes "$var"
     echo "$p"
    
  3. Using echo -e to convert \n to a new line

     p="${var1}\n${var2}"
     echo -e "${p}"
    

    echo -e interprets the two characters "\n" as a new line.

     var="a b c"
     first_loop=true
     for i in $var
     do
        p="$p\n$i"            # Append
        unset first_loop
     done
     echo -e "$p"             # Use -e
    

    Avoid extra leading newline

     var="a b c"
     first_loop=1
     for i in $var
     do
        (( $first_loop )) &&  # "((...))" is bash specific
        p="$i"            ||  # First -> Set
        p="$p\n$i"            # After -> Append
        unset first_loop
     done
     echo -e "$p"             # Use -e
    

    Using a function

     embed_newline()
     {
        local p="$1"
        shift
        for i in "$@"
        do
           p="$p\n$i"         # Append
        done
        echo -e "$p"          # Use -e
     }
    
     var="a b c"
     p=$( embed_newline $var )  # Do not use double quotes "$var"
     echo "$p"
    

    ⚠ Inserting "\n" in a string is not enough to insert a new line: "\n" are just two characters.

The output is the same for all

a
b
c

Special thanks to contributors of this answer: kevinf, Gordon Davisson, l0b0, Dolda2000 and tripleee.


oHo
  • 51,447
  • 27
  • 165
  • 200
  • 21
    This doesn't actually embed newlines, it embeds `\n`, which the `echo -e` converts to newlines as it prints. Depending on your actual goal, this may or may not do the trick. – Gordon Davisson Feb 04 '12 at 17:52
  • Thank you very much @GordonDavisson. You are right! I have then updated my answer to insert a real new lines. To thank you I have upvoted a very good answer from you. Cheers. See you ;-) – oHo Feb 04 '12 at 20:49
  • 1
    You've got a missing double quote; the third last code line should be `p="$p"$'\n'"$i"` – l0b0 Feb 06 '12 at 14:37
  • Yep @l0b0, you have very good eagle eyes ;-) Next time I will help you to improve your answer :-D Thanks – oHo Feb 06 '12 at 21:14
  • 1
    This prepends the output with an extra newline! – Kevin Mar 10 '16 at 00:15
  • 1
    @kevinf Thank you very much, I have just updated (extended) this four-years-old answer! Cheers :-) – oHo Mar 10 '16 at 06:37
  • # adding trailing newline, if value exists # if [ -n "$configs" ]; then configs="$configs\n"; fi # Appending a new value # configs="$configs$newconfig" # two Liner # echo -e "$configs" – Kevin Mar 10 '16 at 15:51
  • Even if this embeds a real newline in p, you'll have a hard time to actually print those with echo! At least, that doesn't work with my bash. For example: echo first > file; echo second >> file; var="$(cat file)"; echo $var; just prints "first second". But var DOES contain a new line (which you can see with: IFS=$'\n'; for f in $var; do echo ":$f:"; done). – Carlo Wood Feb 14 '18 at 14:14
  • Hi @CarloWood. Add the surrounding double quotes in `echo "$var"` and you will see the newline. Full test is: `echo first > file; echo second >> file; var="$(cat file)"; echo "$var"`. When double quote is missing, bash interprets the newline as a separator between two command line arguments: `first` and `second`. When you use the surrounding double quotes, bash understand there is a single command line argument: `$var` (that argument contains the newline). Is it clear? Have fun. Cheers – oHo Feb 15 '18 at 15:36
  • Ah... you're right. My problem was not adding double quotes when printing the variable :/. Thanks. – Carlo Wood Feb 15 '18 at 22:34
  • echo -e "script\nhere" | python3 saved me a lot of pain, many thanks. – Greg Reynolds Jun 02 '20 at 09:30
  • IMHO the first option should be removed – CervEd Apr 13 '22 at 07:23
  • Thank you @CervEd. I have just rephrased the `echo -e` idea and moved it down, with a ⚠ notice. I think this is better. Do you suggest to go further? – oHo Sep 26 '22 at 23:45
  • @oHo A solution using `printf` instead of `echo` is probably preferable – CervEd Sep 27 '22 at 08:34
34

The trivial solution is to put those newlines where you want them.

var="a
b
c"

Yes, that's an assignment wrapped over multiple lines.

However, you will need to double-quote the value when interpolating it, otherwise the shell will split it on whitespace, effectively turning each newline into a single space (and also expand any wildcards).

echo "$p"

Generally, you should double-quote all variable interpolations unless you specifically desire the behavior described above.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • For more on proper quoting, see http://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-variable – tripleee Nov 08 '15 at 16:47
  • not working for me macOS Catalina, echo prints it all on one line echo -e or just echo. :(. – Dean Hiller Apr 07 '20 at 22:41
  • 1
    @DeanHiller Again, the problem is with the quoting, not the assignment. You need `echo "$p"`, not `echo $p`. (In `zsh` you don't even need the quoting. And probably never use `echo -e`; prefer `printf` for anything where basic `echo` is too crude.) – tripleee Apr 08 '20 at 03:02
15

Try echo $'a\nb'.

If you want to store it in a variable and then use it with the newlines intact, you will have to quote your usage correctly:

var=$'a\nb\nc'
echo "$var"

Or, to fix your example program literally:

var="a b c"
for i in $var; do
    p="`echo -e "$p\\n$i"`"
done
echo "$p"
Dolda2000
  • 25,216
  • 4
  • 51
  • 92
  • The `$'variable'` syntax is very convenient, but I don't believe it is portable to other shells. – tripleee Feb 04 '12 at 10:33
  • 1
    @Dolda2000 Why do we have to escape \n ? – Ankur Agarwal Feb 04 '12 at 16:27
  • @abc: That depends on which escape you mean. If you mean the final, `echo "$p"`, it's because the shell would otherwise interpret the newlines as simple parameter separators, pass `a`, `b` and `c` to `echo` as three different parameters, and `echo` would then join them with spaces. When you quote `$p`, its exact contents are passed intact as one single parameter. – Dolda2000 Feb 05 '12 at 03:10
  • perfect, I think from `info echo` we can see all possible escaped chars to use like `var=$'\n'`, thanks! – Aquarius Power May 11 '15 at 01:29
  • Using `echo -e` inside backquotes is just crazy overcomplicated. If you have a newline in a variable `nl=$'\n'` you can just interpolate it `p=$p$nl$i` but even simpler, just put a literal newline in there in the first place. – tripleee Mar 28 '22 at 05:12
14

There are three levels at which a newline could be inserted in a variable. Well ..., technically four, but the first two are just two ways to write the newline in code.

1.1. At creation.

The most basic is to create the variable with the newlines already. We write the variable value in code with the newlines already inserted.

$ var="a
> b
> c"
$ echo "$var"
a
b
c

Or, inside script code:

var="a
b
c"

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

1.2. Create using shell quoting.

The sequence $' is a special shell expansion in Bash and Z shell.

var=$'a\nb\nc'

The line is parsed by the shell and expanded to « var="anewlinebnewlinec" », which is exactly what we want the variable var to be. That will not work on older shells.

2. Using shell expansions.

It is basically a command expansion with several commands:

  1. echo -e

     var="$( echo -e "a\nb\nc" )"
    
  2. The Bash and Z shell printf '%b'

     var="$( printf '%b' "a\nb\nc" )"
    
  3. The Bash printf -v

     printf -v var '%b' "a\nb\nc"
    
  4. Plain simple printf (works in most shells):

     var="$( printf 'a\nb\nc' )"
    

3. Using shell execution.

All the commands listed in the second option could be used to expand the value of a variable, if that var contains special characters.

So, all we need to do is get those values inside the variable and execute some command to show:

var="a\nb\nc"                 # var will contain the characters \n not a newline.

echo -e "$var"                # use echo.
printf "%b" "$var"            # use bash %b in printf.
printf "$var"                 # use plain printf.

Note that printf is somewhat unsafe if the variable value is controlled by an attacker.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
9

There isn’t any need to use a for loop.

You can benefit from Bash parameter expansion functions:

var="a b c";
var=${var// /\\n};
echo -e $var
a
b
c

or just use tr:

var="a b c"
echo $var | tr " " "\n"
a
b
c
yoshi kakbudto
  • 169
  • 2
  • 4
3

sed solution:

echo "a b c" | sed 's/ \+/\n/g'

Result:

a
b
c
0xAX
  • 20,957
  • 26
  • 117
  • 206
-1
var="a b c"
for i in $var
do
   p=`echo -e "$p"'\n'$i`
done
echo "$p"

The solution was simply to protect the inserted newline with a "" during current iteration when variable substitution happens.

Ankur Agarwal
  • 23,692
  • 41
  • 137
  • 208