What characters need to be escaped when using the printf command?

In the format argument of printf, only the % and \ characters are special (no, " is not special and \" is unspecified per POSIX).

But, two important notes.

  1. In most printf implementations¹, it's the byte values for \ and % that are special and the POSIX specification could even be interpreted as requiring it as it requires the printf utility to be an interface to the printf(3) C function and not wprintf(3) for instance (like it requires %.3s to truncate to 3 bytes and not 3 characters).

    In some character encodings including BIG5 and GB18030, there are hundreds of characters that contain the encoding of backslash, and to escape those for printf, you'd need to insert a \ before each 0x5c byte within the encoding of those characters!

    For instance in BIG5-HKSCS, as used for instance in the zh_HK.big5hkscs (Hong Kong) locale, all of Ěαжふ㘘㙡䓀䨵䪤么佢俞偅傜兝功吒吭园坼垥塿墦声娉娖娫嫹嬞孀尐岤崤幋廄惝愧揊擺暝枯柦槙檝歿汻沔涂淚滜潿瀙瀵焮燡牾狖獦珢珮琵璞疱癧礒稞穀笋箤糭綅縷罡胐胬脪苒茻莍蓋蔌蕚螏螰許豹贕赨跚踊蹾躡鄃酀酅醆鈾鎪閱鞸餐餤駹騱髏髢髿鱋鱭黠﹏ contain byte 0x5c (which is also the encoding of \).

    With most printf implementations, in that locale, printf 'αb' doesn't output αb but byte 0xa3 (the first byte of the encoding of α) followed by the BS character (the expansion of \b).

    $ LC_ALL=zh_HK.big5hkscs luit
    $ locale charmap
    BIG5-HKSCS
    $ printf 'αb' | LC_ALL=C od -tx1 -tc
    0000000  a3  08
            243  \b
    0000002
    

    Best is to avoid using (and even installing / making available) those locales as they cause all sorts of bugs and vulnerabilities of that sort.

  2. Some printf implementations support options, and even those that don't are required to support -- as the option delimiter. So printf -- won't output -- but likely report an error about a missing format argument. So if you can't guarantee your format won't start with -, you have to use the -- option delimiter:

     printf -- "$escaped_format" x y...
    

In any case, if you want to print arbitrary strings, you'd use:

printf '%s\n' "$data" # with terminating newline
printf %s "$data"     # without

There's no character that is special in the string passed to %s (though note that with the exception of the printf builtin of zsh, you can't pass the NUL character in any of printf arguments).

Note that while the canonical way to enter a literal \ is with \\ and a literal % with %%, on ASCII-based systems, you can also use \134 and \45 and with some printf implementations \x5c, \x25, or \x{5c}, \x{25}, or (even on non-ASCII systems): \u005c, \u0025 or \u{5c}, \u{25}.


¹ yash's printf builtin being the only exception I am aware of.


From the manual:

$ man printf
...
   printf FORMAT [ARGUMENT]...
...
   FORMAT controls the output as in C printf.  Interpreted sequences are:

This lists several interpreted sequences. The following are those where the character itself needs to be escaped.

   \"     double quote
   \\     backslash
   %%     a single %

I tested these three in bash, and they behaved as expected. As per man bash, this implementation of printf uses the "standard printf(1) format specifications" as above, in addition to a few more that aren't relevant here.


However, other shells such as zsh implement printf slightly differently. Here, the double quote shouldn't be escaped.

$ printf '"'
"   
$ printf '\"'
\"