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:

  1. 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.
  2. 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 invoke command_name.

  • Utilities, regular built-in utilities, command_names including a <slash> character, and any implementation-defined functions that are found using the PATH 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 ...]
$