Follow-up vis-a-vis echo
vs printf
:
(Below, builtin
means "special builtin", and "regular builtin"s are not considered to be builtins by me since they are not built into the shell)
The first POSIX standardization committee could not agree on how to
standardize echo, so they compromised by issuing that if it was passed flags (-e,-n,-E
, etc.) or if any arguments contained escape sequences (\n,\c,\t
, etc.) that the behavior was to be defined by the implementing shell rather than POSIX. Instead, the printf
command was added and given well-defined behavior.
(source: Classic Shell Scripting, by Robbins and Beebe).
Although printf
is well-defined, some shells do not have printf
as a
builtin command (e.g. mksh
). Instead, they use printf
from /usr/bin/
.
This meant all scripts run from that shell would print the same on a
given operating system (Ubuntu, Fedora, etc.), but that they wouldn't
necessarily print the same across OSs (in fact, many users changed the
printf
in their /usr/bin
for this reason).
Alternatively, shells with printf
as a builtin would print the same
regardless of OS, but only if used as implemented for the shell. However,
since printf
behavior is defined by the POSIX standard, that isn't necessarily
a concern for programmers. However, if PATH
were overriden for shells that use printf
from /usr/bin/
, printf
wouldn't be found.
Though all shells have echo
as a builtin, some interpret escape sequences
directly (e.g. ash
) while others (most) require a -e
flag: the behavior is
not defined by POSIX, but by the shell.
One of the main annoyances of echo
vs. printf
is that echo
prints new
lines at the end of the string by default, but printf
does not. printf
requires the \n
escape sequence to print new lines. Conversely, to prevent
echo
from printing a new line, the \c
escape sequence is required
(potentially, also requiring the -e
flag).
printf
is recommended for maximum portability since its behavior is defined by POSIX, but I personally find explicitly printing a new line at the end of each line is quite annoying (most lines I write require a new line at the end and I very rarely need to suppress echo
's printing of new lines). On the other hand,
echo
is always available since it's a builtin (no risk of not being found on $PATH) and a simple check can be performed to determine whether the -e
flag is needed and a corresponding aliased echo
made:
#! /bin/sh -
Determine if "builtin" command exists.
BUILTIN='builtin'
if ! ("${BUILTIN}" echo 123 >/dev/null 2>&1); then
BUILTIN=''
fi
export BUILTIN
ECHO='echo -e'
if ${BUILTIN} [ "echo -e test
" = '-e test' ]; then
ECHO='echo'
fi
export ECHO
Now use "${ECHO}" where you would normally use "echo"...
Personally, I prefer to do this and only use printf
if I need special formatting.
UPDATE:
I should give proper credit where credit is due. The shell code above was taken directly from shunit2
. Credit goes to Kate Ward and the shunit2
development team for that one! (Well done ;) )
printf
available. – studog Jan 23 '19 at 22:49PATH
and then call the built-in utility, not the external script. What if you'd want to call the external script in your path? Hmm... This seems to call for a table describing the different possibilities. There is one here, but it doesn't make sense to me. – Kusalananda Jan 23 '19 at 23:23builtin
as the spec explicitly disables it (and yash can use it while in posix). But you have, at least, three options to call external utilities: (1) The widely known full path:/usr/bin/printf
(2) Have and use an external which that correctly finds external utilities and execute$(which printf)
and (3) the much recomended tool to call programs in the PATH:env printf
. – Jan 24 '19 at 02:36ls
calls the shell own function (or built-in),command ls
should call the externalls
(but doesn't work in yash bug?). The following two are externally used commands, in a makefile and in Perl (maybe?). The last one looks incorrect to me. The PATH to search for a command name (in the parent shell) can not change by an assignment. – Jan 24 '19 at 02:52enable
could do that reliably. – Jan 24 '19 at 03:28