bash printf with new line

43,671

printf's %b format specifier was meant specifically to replace echo -e (actually, the XSI extension to echo which calls for special interpretation of the arguments by default. -e was never specified and is disallowed by POSIX.), and is identical in virtually every way including a few differences from $'...' and the format string argument to printf.

 $ ( var='Age:\n20\ncolor:\nred'; printf '%b\n' "$var" )
Age:
20
color:
red

You should generally avoid expanding variables into the format string unless your program controls the exact value and it is intended specifically to be a format string. Your last example in particular has the potential to be quite dangerous in Bash due to printf's -v option.

# Bad!
var='-v_[$(echo "oops, arbitrary code execution" >&2)0]'
printf "$var" foo

It is usually good practice to avoid %b unless you have a special portability requirement. Storing the escape codes in a variable instead of the literal data violates principles of separation of code and data. There are contexts in which this is ok, but it is usually better to assign the the value using $'...' quoting, which is specified for the next version of POSIX, and has long been available in Bash and most ksh flavours.

x=$'foo\nbar'; printf '%s\n' "$x"    # Good
x=(foo bar); printf '%s\n' "${x[@]}" # Also good (depending on the goal)
x='foo\nbar'; printf '%b\n' "$x"     # Ok, especially for compatibility
x='foo\nbar'; printf -- "$x"         # Avoid if possible, without specific reason
Share:
43,671
idobr
Author by

idobr

Updated on July 09, 2022

Comments

  • idobr
    idobr 4 months

    I'm a bit confused with printing a variable that contain a new line symbol in bash.

    var="Age:\n20\ncolor:\nred"
    echo -e $var
    Age:
    20
    color:
    red
    

    This is working, but a lot of people say that echo with options is not portable and it is better to use printf.

    I never used prinf. According to manuals to emitate echo command:

    printf '%s\n' "$var"
    Age:\n20\ncoloe:\nred
    

    But this doesn't parse \n inside variable. manuals usually have this example:

    printf "Surname: %s\nName: %s\n" "$SURNAME" "$LASTNAME"
    

    But it's not my case and from my point of view it not comfortable to use. I found out simply by typing that I can use this:

    printf "$var\n"
    

    Is it portable? If I then pass $var to a mail command will it save new line breaks?

    printf "$var\n" | mail -s subj [email protected] 
    
  • idobr
    idobr over 9 years
    Thank you for the detailed answer.
  • chepner
    chepner over 9 years
    @ormaaj: I've seen someone (you?) mention features that will be added to the next version of POSIX before; is there a publicly visible draft of this? Just curious.
  • Melab
    Melab over 5 years
    It sounds to me like %b is a very good thing that needn't be avoided if it is meant to replace echo -e.
  • ormaaj
    ormaaj over 5 years
    @Melab It is a good thing. I'd just think about why I'm using it first, because it means you're dealing with unexpanded literal code as a value, which isn't always the most sensible approach.