33
votes

Working with printf in a bash script, adding no spaces after "\n" does not create a newline, whereas adding a space creates a newline, e. g.:

  1. No space after "\n"

    NewLine=`printf "\n"`
    echo -e "Firstline${NewLine}Lastline"
    

    Result:

    FirstlineLastline
    
  2. Space after "\n "

    NewLine=`printf "\n "`
    echo -e "Firstline${NewLine}Lastline"
    

    Result:

    Firstline
     Lastline
    

Question: Why doesn't 1. create the following result:

Firstline 
Lastline

I know that this specific issue could have been worked around using other techniques, but I want to focus on why 1. does not work.

Edited: When using echo instead of printf, I get the expected result, but why does printf work differently?

    NewLine=`echo "\n"`
    echo -e "Firstline${NewLine}Lastline"

Result:

    Firstline
    Lastline
8

8 Answers

34
votes

The backtick operator removes trailing new lines. See 3.4.5. Command substitution at http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html

Note on edited question

Compare:

[alvaro@localhost ~]$ printf "\n"

[alvaro@localhost ~]$ echo "\n"
\n
[alvaro@localhost ~]$ echo -e "\n"


[alvaro@localhost ~]$

The echo command doesn't treat \n as a newline unless you tell him to do so:

NAME
       echo - display a line of text
[...]
       -e     enable interpretation of backslash escapes

POSIX 7 specifies this behaviour here:

[...] with the standard output of the command, removing sequences of one or more characters at the end of the substitution

8
votes

Maybe people will come here with the same problem I had: echoing \n inside a code wrapped in backsticks. A little tip:

printf "astring\n"
# and 
printf "%s\n" "astring" 
# both have the same effect.
# So... I prefer the less typing one

The short answer is:

# Escape \n correctly !

# Using just: printf "$myvar\n" causes this effect inside the backsticks:
printf "banana
"

# So... you must try \\n  that will give you the desired 
printf "banana\n"

# Or even \\\\n if this string is being send to another place 
# before echoing,

buffer="${buffer}\\\\n printf \"$othervar\\\\n\""

One common problem is that if you do inside the code:

echo 'Tomato is nice'

when surrounded with backsticks will produce the error

command Tomato not found.

The workaround is to add another echo -e or printf

printed=0

function mecho(){
  #First time you need an "echo" in order bash relaxes.
  if [[ $printed == 0 ]]; then
    printf "echo -e $1\\\\n"
    printed=1
  else
    echo -e "\r\n\r$1\\\\n"
  fi
}

Now you can debug your code doing in prompt just:

(prompt)$  `mySuperFunction "arg1" "etc"`

The output will be nicely

 mydebug: a value
 otherdebug: whathever appended using myecho
 a third string

and debuging internally with

mecho "a string to be hacktyped"
6
votes
$ printf -v NewLine "\n"
$ echo -e "Firstline${NewLine}Lastline"

Firstline
Lastline

$ echo "Firstline${NewLine}Lastline"
Firstline
Lastline
4
votes

It looks like BASH is removing trailing newlines. e.g.

NewLine=`printf " \n\n\n"`
echo -e "Firstline${NewLine}Lastline"
Firstline Lastline

NewLine=`printf " \n\n\n "`
echo -e "Firstline${NewLine}Lastline"
Firstline


 Lastline
4
votes

Your edited echo version is putting a literal backslash-n into the variable $NewLine which then gets interpreted by your echo -e. If you did this instead:

NewLine=$(echo -e "\n")
echo -e "Firstline${NewLine}Lastline"

your result would be the same as in case #1. To make that one work that way, you'd have to escape the backslash and put the whole thing in single quotes:

NewLine=$(printf '\\n')
echo -e "Firstline${NewLine}Lastline"

or double escape it:

NewLine=$(printf "\\\n")

Of course, you could just use printf directly or you can set your NewLine value like this:

printf "Firstline\nLastline\n"

or

NewLine=$'\n'
echo "Firstline${NewLine}Lastline"    # no need for -e
3
votes

For people coming here wondering how to use newlines in arguments to printf, use %b instead of %s:

$> printf "a%sa" "\n"
a\na
$> printf "a%ba" "\n"
a
a

From the manual:

%b expand backslash escape sequences in the corresponding argument

1
votes

We do not need "echo" or "printf" for creating the NewLine variable:

NewLine="
"
printf "%q\n" "${NewLine}"
echo "Firstline${NewLine}Lastline"
-3
votes

Works ok if you add "\r"

$ nl=`printf "\n\r"` && echo "1${nl}2"
1
2