Search code examples
shellprintfposix

Why does shell printf run strings together when not quoted?


Can somebody explain this behavior in shell script:

TESTVAR='something fishy'
echo $TESTVAR
something fishy

But

printf "%b" $TESTVAR
somethingfishy            (space removed)

While

printf "%b" "$TESTVAR"
something fishy

Why is that so?


Solution

  • This behavior is a consequence of two rules:

    • When you pass printf more arguments than the format string contains placeholders, the format string is repeated until all arguments are consumed. With a format string having only one placeholder, that means it's repeated once per subsequent argument.

    • When you don't quote parameter expansions, they undergo word-splitting (creating a separate word for contents on either side of each character in IFS) and glob expansion (replacing those words with a list of filenames which they match when interpreted as globs, should any such names exist), thus resulting in a variable number of shell words, each of which is passed as a separate argument to printf.


    Thus, TESTVAR='something fishy'; echo $TESTVAR, with a default IFS, becomes a precise equivalent to echo "something" "fishy", which prints something fishy, because echo separates each argument with a space.

    By contrast, printf %b $TESTVAR becomes printf %b "something" "fishy", which uses %b to format something, and again to format fishy; nothing in there inserts a space, so you don't get any spaces on output. (If instead you'd made it printf '%b ' $TESTVAR, you would have seen spaces inserted).

    Finally, printf %b "$TESTVAR" runs printf %b "something fishy"; the space is preserved because it's a literal part of the third argument, so the %b format string is only evaluated once, against the entire something fishy argument with a space already included.


    The lesson of this story: Always quote your expansions. See also BashPitfalls #14 describing in detail how echo $foo is itself buggy unless quoted as echo "$foo".