Slightly confused about whether printf in the yash shell is a built-in command or not
The yash
shell does have, and does use, a built-in version of printf
(and other utilities). It just happens to be very pedantically POSIX compliant in the way it formulates the result of the command -v
and type
commands.
As mosvy comments, the POSIX standard requires that a regular built-in command be available as an external command in $PATH
for the built-in version of the command to be executed.
This is the relevant text from the standard:
Command Search and Execution
If a simple command results in a command name and an optional list of arguments, the following actions shall be performed:
If the command name does not contain any <slash> characters, the first successful step in the following sequence shall occur:
a. If the command name matches the name of a special built-in utility, that special built-in utility shall be invoked.
[...]
- e. Otherwise, the command shall be searched for using the PATH environment variable as described in XBD Environment Variables :
- i. If the search is successful:
- a. If the system has implemented the utility as a regular built-in or as a shell function, it shall be invoked at this point in the path search.
- b. Otherwise, the shell executes the utility in a separate utility environment [...]
[...]- ii. If the search is unsuccessful, the command shall fail with an exit status of 127 and the shell shall write an error message.
- If the command name contains at least one <slash>, [...]
This means that the output of command -v printf
signifies that the printf
command was found in the search path, while the output of type printf
adds to this that the command is a regular built-in.
Since the printf
command was found in the search path, and since it's a regular built-in in the shell, yash
will call its built-in version of the command. If the printf
was not found in the path, and if the yash
shell was running in POSIX-ly correct mode, an error would have been generated instead.
yash
prides itself on being a very POSIX compliant shell, and this is also true if we look at what POSIX says about command -v
:
-v
Write a string to standard output that indicates the pathname or command that will be used by the shell, in the current shell execution environment (see Shell Execution Environment), to invoke
command_name
, but do not invokecommand_name
.
- Utilities, regular built-in utilities,
command_names
including a<slash>
character, and any implementation-defined functions that are found using thePATH
variable (as described in Command Search and Execution), shall be written as absolute pathnames.
The Watanabe shell has three sorts of built-ins, described in detail in its manual. All of the built-in commands are also listed there, but one has to infer that something is a "regular" built-in command from the absence of any note saying that the command is a "special" or a "semi-special" built-in. Regular built-ins are unmarked.
printf
is one such "regular" built-in. In native mode it is always invoked, irrespective of whether there is an external command found by that name.
$ PATH=/usr/bin $ printf printf: this command requires an operand $ type printf printf: a regular built-in at /usr/bin/printf $ $ PATH=/ $ printf printf: this command requires an operand $ type printf printf: a regular built-in (not found in $PATH) $
But when the posixly-correct
shell option is set it is only a built-in if the external command can be found on the PATH
.
$ set --posixly-correct $ $ PATH=/usr/bin $ printf printf: this command requires an operand $ $ PATH=/ $ printf yash: no such command `printf' $
This is actually conformant to what the Single Unix Specifiation says, and has said since at least 1997.
It differs from the Z shell, the 93 Korn shell, the Bourne Again shell, and the Debian Almquist shell, none of which either implement or document such behaviour for regular built-ins. The Z shell, for example, documents that regular built-ins are always found, before the step that searches PATH
. So too does the Debian Almquist shell. And that's what these shells all do, even if invoked as sh
with their turn-on-POSIX options.
% /bin/exec -a sh zsh -c "PATH=/ ; type printf ; printf" printf is a shell builtin zsh:printf:1: not enough arguments % /bin/exec -a sh ksh93 -c "PATH=/ ; type printf ; printf" printf is a shell builtin Usage: printf [ options ] format [string ...] % /bin/exec -a sh bash --posix -c "PATH=/ type printf ; printf" printf is a shell builtin printf: usage: printf [-v var] format [arguments] % /bin/exec -a sh dash -c "PATH=/ ; type printf ; printf" printf is a shell builtin sh: 1: printf: usage: printf format [arg ...] %
However, not running printf
when it is not on the PATH
is the behaviour of the PD Korn shell, the Heirloom Bourne shell, and the MirBSD Korn shell; because they do not have a printf
built-in in the first place. ☺
% /bin/exec -a sh `command -v ksh` -c "PATH=/ ; type printf ; printf" printf not found sh: printf: not found % /bin/exec -a sh `command -v oksh` -c "PATH=/ ; type printf ; printf" printf not found sh: printf: not found % /bin/exec -a sh `command -v jsh` -c "PATH=/ ; type printf ; printf" printf not found sh: printf: not found % /bin/exec -a sh mksh -c "PATH=/ ; type printf ; printf" printf not found sh: printf: not found % ksh -c "type printf ; printf" printf is a tracked alias for /usr/bin/printf usage: printf format [arguments ...] % oksh -c "type printf ; printf" printf is a tracked alias for /usr/bin/printf usage: printf format [arguments ...] % jsh -c "type printf ; printf" printf is hashed (/usr/bin/printf) usage: printf format [arguments ...] % mksh -c "type printf ; printf" printf is a tracked alias for /usr/bin/printf usage: printf format [arguments ...] $