shell programming - output

created onApril 18, 2022

For output, two commands can be used: and . Both commands are available as a shell built-in and/or as a program, on most systems, both as a built-in and as a program.

echo

In the beginning, echo did nothing but just output the arguments on the screen, separated by a blank and followed by a newline character after the last argument. Then someone thought it’s a good idea to add control sequences like , and for ‘tabs’, ‘carriage return’ and ‘clear to end of line’ to echo.

As it is always the case with any unix tool, it’s behaviour varies between different vendors or Unix systems. Some vendors chose to add options like to omit the trailing newline character, to enablde and to disable control sequences in arguments in whatever combination of options they felt like. So the behaviour of echo varies between the implementations of the vendors, the different shells like ash, bash, dash, zsh, csh, the different versions of each shell, and –to make the chaos complete– with which switches the shell or echo program is compiled and with which switches the shell is configured like i.e. for bash.

The POSIX spec for echo is quite clear about this:

" It is not possible to use echo portably across all POSIX systems unless both -n (as the first argument) and escape sequences are omitted. ... New applications are encouraged to use printf instead of echo. "
The Open Group Base Specifications Issue 7, 2018 edition, echo
use of echo for uncontrolled input data like user input or data from external systems is highly error prone. Input data containing control sequences will most likely screw up the output.

printf

printf is derived from the same name C function and it’s syntax is the same as it’s C sibling.

It is compatible not only across the Unix ecosystem, but also across different OS in general. That is, as long as one sticks to the printf features defined in POSIX.

The general form of printf is:

printf <format> [<argument>] ...

i.e.

printf '%s\n' "hi there"

prints the string , followed by a newline.

As long as don’t you use esoteric features with printf, you’re on the safe side.

printf format

The format follows this pattern: %[flags][width][.precision]<specifier>

specifiers

specifier output format input output
c character a a
s string hi there hi there
% a percent character % followed by another % will output a single % %
u unsigned decimal integer 42 42
d or i signed decimal integer -7 -7
f decimal floating point, lowercase 123.45 123.45
e scientific notation, lowercase 123.45 1.234500e+2
E Scientific notation, uppercase 123.45 1.234500E+2
g shortest representation of %e or %f 1230000.45 1.23e+06
G shortest representation of %E or %F 1230000.45 1.23E+06
o unsigned octal 1984 3700
x unsigned hexadecimal integer lowercase 1984 7c0
X unsigned hexadecimal integer uppercase 1984 7C0
a hexadecimal floating point, lowercase 4.25 0x8.8p-1
A hexadecimal floating point, uppercase -4.25 -0x8.8P-1

There is also a specifier F for decimal floating point, uppercase defined, but I do not have the slightest idea how a decimal floating number can be printed in upper case, so I didn’t include that one into the table above.

Surprisingly, the implementation of the specifiers e, E and even f and F is not mandatory:

" The a, A, e, E, f, F, g, and G conversion specifiers need not be supported. "
The Open Group Base Specifications Issue 7, 2018 edition, printf, extended description, 6.

On the other hand I haven’t come across a version of printf that didn’t support scientific notation or floating point decimals in 25 years and it’s pretty unlikely that one will dig out such an archaic version of printf.

flags

flag description
- left-justify output. The default is to justify right
+ prepend a plus or minus sign (+ or -) to the output. By default, only negative numbers are prepended with a - sign.
(space) if no sign is going to be written, prepend the output with a blank (space).
0 left-pad the number with zeroes (0) instead of spaces when padding is specified
# used with o, x or X specifiers the value is preceeded with 0, 0x or 0X respectively for values different than zero

width

width description
positive integer minimum number of characters to be printed. If the value to be printed is shorter, the output is padded with blanks (spaces). The output is not truncated if the result is larger.
* the width is not specified in the format string, but as an additional argument (integer) preceding the argument that has to be formatted.

precision

precision applies to specifier(s) description
.number d, i, u, x, X, o the minimum number of digits to be written. If the value to be written is shorter than this number, the result is padded with leading zeros. The value is not truncated even if the result is longer. A precision of 0 means that no character is written for the value 0.
.number a, A, e, E, f, F the number of digits to be printed after the decimal point. The default is 6.
.number g, G the maximum number of significant digits to be printed
.number s the maximum number of characters to be printed. By default all characters are printed
.* the precision is not specified in the format string, but as an additional argument (integer) preceding the argument that has to be formatted

If the period is specified without an value for precision, a precision of 0 is applied.

control sequences

control sequence function
\a audible alert
\b backspace
\f form feed
\n newline (linefeed)
\r carriage return
\t tab
\v vertical tab
\ backslash

printf example

printf with format string for three numeric arguments and one string argument:

printf "'%10.3f'\n'%10.3f'\n'%10.3f'\n%s\n" 123.45 42 19.55588 done

output:

' 123.450' ' 42.000' ' 19.556' done

reference

The Open Group Base Specifications Issue 7, 2018 edition, echo
The Open Group Base Specifications Issue 7, 2018 edition, printf
Sven Mascheck, Variations in echo implementations