$ printf "hi"
hi$ printf "hi\n"
hi
$ printf "hi\\n"
hi
Why doesn't the last line print hi\n
?
$ printf "hi"
hi$ printf "hi\n"
hi
$ printf "hi\\n"
hi
Why doesn't the last line print hi\n
?
This is nothing to do with printf
, and everything to do with the argument that you have given to printf
.
In a double-quoted string, the shell turns \\
into \
. So the argument that you have given to printf
is actually hi\n
, which of course printf
then performs its own escape sequence processing on.
In a double-quoted string, the escaping done through \
by the shell is specifically limited to affecting the ␊, \
, `
, $
, and "
characters. You will find that \n
gets passed to printf
as-is. So the argument that you have given to printf
is actually hi\n
again.
Be careful about putting escape sequences into the format string for printf
. Only some have defined meanings in the Single Unix Specification. \n
is defined, but \c
is actually not, for example.
printf "hi\\\\n"
will print the single literal backslash.
– BallpointBen
Jan 31 '20 at 17:02
printf
. The exception would be if you want to have a literal single quote in the format string.
– Barmar
Jan 31 '20 at 19:55
\n
is the same as \\n
, so \\\n
is the same as \\\\n
. Honestly, it's better to just use single-quotes to avoid this shell behaviour.
– JoL
Jan 31 '20 at 21:19
\n
gets passed to printf as-is.
–
Feb 01 '20 at 17:08
\c
is defined (in POSIX point 7) when used with the %b
conversion specifier.
–
Feb 01 '20 at 17:14
!
(or whatever the first character of $histchars
is), but in the case of bash
, the backslash is not removed. (echo -E "\!foo"
still outputs \!foo
in bash
even though backslash served its purpose at disabling history substitution)
– Stéphane Chazelas
Feb 01 '20 at 18:05
Within double quotes, \\n
is an escaped (quoted) backslash followed by a n
. This is given to printf
as \n
and printf
will output a newline.
Within double quotes (still), \n
is the string \n
. Again, printf
receives a \n
string and will print a newline.
Within double quotes, backslash is special only when preceding another backslash, a newline, or any of $
, `
or "
. "Special" means that it removes the special meaning of the next character. If a backslash precedes any other character (n
for example), then it's just a backslash character.
This is explained in the POSIX standard.
To print \n
in a printf
format string, use printf '\\n'
or printf "\\\\n"
, or use printf '%s' '\n'
In general, the printf
format string should be single quoted and any variable data should be given as additional arguments to be inserted into the format string:
printf 'This is how you write a newline: %s\n' '\n'
Ok, lets add another point of view.
There are two levels of interpretation here at play. One is the shell, the other is the command (in this case printf
) interpretation of the arguments received.
Inside double quotes The shell will leave alone most of the sequences backslash-character, this is the common result:
$ printf '%s\n' "\a \b \c \d ... \z \$ \` \\ "
\a \b \c \d ... \z $ ` \
Except with $
, `
, and \
which, being especial to the shell, get their \
removed.
So, testing both the strings you used (and others), we get:
$ printf '%s\n' "hi\n" "hi\\n" "hi\\\n" "hi\\\\n" "hi\\\\\n"
hi\n
hi\n
hi\\n
hi\\n
hi\\\n
The shell converts pairs of \\
to one \
. And leaves alone \n
as \n
.
Now, printf
has an special relation to the first argument, it is explicitly set to be the format
. In the format argument, some characters are special (to printf), for example: valid sequences that start with a %
character and some sequences of backslash-character like:
\\ \a \b \f \n \r \t \v and the special \ddd
So, the string \n
generates a newline but a \\n
don't:
$ printf " hi\n hi\\n hi\\\n hi\\\\n"; echo
hi
hi
hi\n hi\n
printf
(which is almost surely true),printf "hi\\n"
is perfectly valid. – S.S. Anne Jan 30 '20 at 20:45printf "hi\\n"
being invalid. – S.S. Anne Jan 30 '20 at 20:50printf
with a single argument is perfecty valid. The format string can contain other characters and does not need to include any format specifiers. The OP's problem has to do with how the argument is escaped by the shell, not with the wayprintf
is being used. – Grodriguez Jan 31 '20 at 09:17rm -rf *
(in a perfect world), but if you want to avoid unexpected behavior you should just do things properly every time. – jesse_b Jan 31 '20 at 13:58