70

I'm trying to use printf to format some pretty output in a bash script

e.g.:

-----------------------  
| This is some output | 
-----------------------

But I've stumbled over some behavior I don't understand.

$ printf "--"

gives me the error:

printf: usage: printf [-v var] format [arguments]

and

$ printf "-stuff"

results in

-bash: printf: -s: invalid option

So apparently printf thinks I'm trying to pass some arguments while I'm not.

Meanwhile, completely by accident, I've found this workaround:

$ printf -- "--- this works now ----\n"

gives me

--- this works now ----

Can anyone explain this behavior?

Paulo Tomé
  • 3,754
  • 6
  • 26
  • 38
Kenny Rasschaert
  • 1,183
  • 2
  • 9
  • 13
  • 1
    See also http://unix.stackexchange.com/questions/11376/what-does-double-dash-mean – manatwork Oct 17 '11 at 11:51
  • Out of intereset, are there any implementations of `echo` that would fail when doing `echo ------------`? Most only support `-n` (no trailing newline), `-e` (interpret backslash-escaped chars) and possible `-E` (do NOT interpret them) and do not error out when other option-like arguments are encountered, right? (EDIT: GNU's `/bin/echo` also supports `--help` and `--version`.) – janmoesen Oct 18 '11 at 06:34

4 Answers4

76

The -- is used to tell the program that whatever follows should not be interpreted as a command line option to printf.

Thus the printf "--" you tried basically ended up as "printf with no arguments" and therefore failed.

Kusalananda
  • 320,670
  • 36
  • 633
  • 936
sr_
  • 15,224
  • 49
  • 55
  • 29
    In other words, to print `--` you can run `printf -- --`. – l0b0 Oct 17 '11 at 12:14
  • 2
    ... and `printf --` is the same as `printf` (you get the same message) – Peter.O Oct 17 '11 at 14:54
  • 4
    Note that `printf -- $fmt` is not portable. On busybox 1.30.1, `printf -- '%s\n' hello` yields `--`. See also: https://pubs.opengroup.org/onlinepubs/009695399/utilities/printf.html – kelvin Nov 17 '19 at 22:18
  • @kelvin, What? [busybox/printf.c#L421](https://github.com/brgl/busybox/blame/abbf17abccbf832365d9acf1c280369ba7d5f8b2/coreutils/printf.c#L421) – Alexander Shukaev Apr 26 '20 at 00:11
  • 1
    @AlexanderShukaev My bad, I tested it on LineageOS and now I see that it uses `mksh` in `adb shell` instead of busybox. It works normally with `busybox printf -- '%s\n' hello`. The point about portability still stands though. – kelvin Apr 26 '20 at 03:53
  • @kelvin - Why would `printf -- '%s\n' hello` yields `--`? It is not shown in that man page. – midnite Apr 19 '22 at 17:37
  • @midnite "Why would `printf -- '%s\n' hello` yields `--`? It is not shown in that man page." I had tested that command on `adb shell` and that was the output. If you want to know why exactly that happens, you'd probably have to read the source code of the mksh version used by `adb shell`. – kelvin Apr 19 '22 at 19:56
  • @kelvin - I see. Thanks for reply. So this seems to be a special behaviour of mksh. It does not happen to most of the other shells, e.g. bash. – midnite Apr 19 '22 at 20:15
  • @kelvin - Just check in from my LineageOS, which `adb shell echo "$SHELL"` shows `/system/bin/sh`. It seems that its `printf` is treating its `$1` as formatting string ALWAYS. `adb shell printf aa bb cc` gives `aa`. `adb shell printf -v bb cc` gives `-v`. `adb shell printf %s bb cc` gives `bbcc`. `adb shell printf %c bb cc` gives `bc`. Note that I by purpose miss out the quotation marks. – midnite Apr 21 '22 at 15:01
  • @midnite "Just check in from my LineageOS, which `adb shell echo "$SHELL"` shows `/system/bin/sh`. It seems that its `printf` is treating its `$1` as formatting string ALWAYS." Interesting; when I read your first comment I had already forgotten the reason for this behavior, but indeed always treating `$1` as the format (just like printf(3p)) seems to be it. Thanks for the details. By the way, the same behavior happens on the built-in "Terminal Emulator" on LineageOS, which appears to use the same shell (see the output of `sh -v`). – kelvin Apr 22 '22 at 17:28
44

-- is being interpreted as an option (in this case, to signify that there are no more options).

A format string should always be included when using printf to prevent bad interpretation. For your particular case:

printf '%s\n' '-----------------------'
Chris Down
  • 122,090
  • 24
  • 265
  • 262
11

There are differences between printf builtin and /usr/bin/printf, the second one do "what you mean" without these annoying errors.

printf "-----"             # error
printf -- "-----"          # ok
/usr/bin/printf "-----"    # ok
/usr/bin/printf -- "-----" # ok
BOC
  • 211
  • 2
  • 3
1

POSIX provides the option of using octal encoding.

 printf "--"

can become:

printf "\055-"

This is a portable way to avoid the ambiguity of whether -- is being used as a format string or as a marker for the end of options.

jhnc
  • 205
  • 1
  • 6