Bash's Built-in printf Function

 

Even if you're already familiar with the printf command, if you got your information via "man printf" you may be missing a couple of useful features that are provided by bash's built-in version of the standard printf(1) command.

 

If you didn't know bash had its own version of printf, then you didn't heed the note in the man page for the printf(1) command:

NOTE: your shell may have its own version of printf, which usually supersedes the version described here. Please refer to your shell's documentation for details about the options it supports.

You did read the man page, didn't you? I must confess, I'd used printf for quite a while before I realized bash had its own.

To find the documentation for the built-in version of printf, just search for "printf" in the bash man page.

In case you're completely unfamiliar with the printf command, and similar functions in other languages, a couple quick examples should get you up to speed:

$ printf "Hello world\n"
Hello world

$ printf "2 + 2 is %d\n" $((2+2))
2 + 2 is 4

$ printf "%s: %d\n" "a string" 12
a string: 12

You provide printf with a format string and a list of values. It then replaces the %... sequences in the string with the values from the list formatted according to the format specification (the part following the percent sign). There are a dozen or more format specifier characters, but 99% of the time, the only ones you'll need are the following:

  • d - Format a value as a signed decimal number.
  • u - Format a value as an unsigned decimal number.
  • x - Format a value as a hexadecimal number with lower case a-f.
  • X - Format a value as a hexadecimal number with upper case A-F.
  • s - Format a value as a number [edit: correction, as a "string"].

Format specifiers can be preceded by a field width to specify the minimum number of characters to print. A positive width causes the value to be right-justified; a negative width causes the value to be left-justiifed. A width with a leading zero causes numeric fields to be zero-filled. Usually, you want to use negative widths for strings and positive widths for numbers.

Probably not what you want:

$ printf "%20s: %4d\n" "string 1" 12 "string 2" 122
        string 1:   12
        string 2:  122

Still probably not not what you want:

$ printf "%-20s: %-4d\n" "string 1" 12 "string 2" 122
string 1            : 12
string 2            : 122

Probably this is what you want:

$ printf "%-20s: %4d\n" "string 1" 12 "string 2" 122
string 1            :   12
string 2            :  122

Note that printf reuses the format if it runs out of format specifiers, which in the examples above allows you to print two lines (four values) with only two format specifiers.

If you specify the width as an asterisk, then the width is taken from the next value in the list:

$ printf "%*s: %*d\n" -20 "a string" 4 12
a string            :   12

Note that if you want to zero-fill a field and specify the width with an asterisk, put the zero before the asterisk:

$ printf "%*s: %0*d\n" -20 "a string" 4 12
a string            : 0012

So now to the features that bash's built-in version of printf provides. The first is the -v option, which allows you to put the formatted result into a variable rather than print it out. So instead of:

$ hw=$(printf "Hello world")
echo $hw
Hello world

You can do this:

$ printf -v hw "Hello world"
echo $hw
Hello world

The second option is for formatting times (and dates):

$ printf "%(%m-%d-%Y %H:%M:%S)T\n" $(date +%s)
01-10-2019 09:11:44

The format specifier here is %(datefmt)T and the value is a system time in seconds from the epoch. The nested datefmt supports the same format options that are supported by strftime(3). You can get a system time value by specifying the +%s format option to the date command.

A couple special arguments are supported by the %(datefmt)T format. From the bash man page:

Two special argument values may be used: -1 represents the current time, and -2 represents the time the shell was invoked. If no argument is specified, conversion behaves as if -1 had been given.

There are a couple of additional features supported by bash's built-in version of printf, but none that you are likely to need on a regular basis. See the man page for more information.

Mitch Frazier is an embedded systems programmer at Emerson Electric Co. Mitch has been a contributor to and a friend of Linux Journal since the early 2000s.

Load Disqus comments