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?
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"
.