Determine whether Bash resolves printf as an alias, function, builtin, or executable.
Show all same-name candidates, not only executables on PATH.
Which command is appropriate?
Bash type reports how a name is interpreted as a command. type -a shows all matching interpretations and PATH entries.
Detailed explanation
which printfIncorrect. which generally searches PATH and does not show aliases or functions reliably.
which output does not establish a list of aliases, functions, builtins and executable files in the current Bash. Its implementation-specific features are separate from querying Bash itself.
whereis printfIncorrect. whereis searches standard filesystem locations rather than Bash's command resolution.
whereis searches locations of program-related files and manuals. It does not enumerate definitions in the current Bash.
type -p printfIncorrect. Lowercase -p can print nothing for an alias or builtin even when a same-name file is on PATH; it does not enumerate all candidates or perform the forced lookup requested by -P.
Lowercase -p does not force executable lookup when a name is classified as an alias, function, or builtin. In this verification it prints nothing despite the external fixture; compare uppercase -P.
type -a printfCorrect. type -a printf shows all interpretations and matching executables.
The -a option includes definitions currently present in the shell. The verification deliberately supplies an alias, function, builtin and external file, then compares all candidates with -p and -P.
Try it yourself
An example you can run in a temporary verification environment.
LAB_DIR=$(mktemp -d) || exit 1
printf '#!/bin/sh\nexit 0\n' > "$LAB_DIR/printf"
chmod u+x "$LAB_DIR/printf" || exit 1
(
export LC_ALL=C
PATH="$LAB_DIR"
hash -r
printf() { :; }
shopt -s expand_aliases
alias printf='builtin printf'
LAB_ALL=$(type -a printf)
if [[ "$LAB_ALL" == *"aliased to"* && "$LAB_ALL" == *"is a function"* && "$LAB_ALL" == *"is a shell builtin"* && "$LAB_ALL" == *"$LAB_DIR/printf"* ]]; then
builtin printf 'all=alias+function+builtin+external\n'
fi
LAB_SMALL_P=$(type -p printf)
[[ -z "$LAB_SMALL_P" ]] && builtin printf 'type-p=empty\n'
LAB_BIG_P=$(type -P printf)
[[ "$LAB_BIG_P" == "$LAB_DIR/printf" ]] && builtin printf 'type-P=external\n'
)
rm -- "$LAB_DIR/printf"
rmdir -- "$LAB_DIR"Official references
- Official documentation 1
- Official documentation 2
- Official documentation 3
- Official documentation 4
- Official documentation 5
- Official documentation 6
Expected result
all=alias+function+builtin+external
type-p=empty
type-P=externalKey points
- Shell builtin
- Alias and function
- -a shows all candidates
Notes
- Environment: Bash 5.2 / GNU coreutils in a fresh process without user startup files, with errexit off. Comparison PATH, LC_ALL, hash, alias and function settings stay in a subshell; HOME is unchanged.
- The noninteractive subshell explicitly enables alias expansion for this synthetic comparison. Output uses builtin printf; the external fixture is not executed.
- The full type -a text includes a function definition, so the example emits a fixed marker after checking all four kinds. It separately checks empty -p output and the -P path.
- Start with the builtin/external case, then compare the additional candidates produced by aliases and functions. Do not assume all four kinds are present in every shell.
Foundation review
An empty -p result does not prove absence on PATH
When an alias or builtin is selected, -p can be empty even though an external executable exists. Distinguish that result from the external-path query made with -P.
All candidates means current definitions
Aliases and functions appear when they have been defined. Requesting all candidates does not create missing kinds of definition.
Do not confuse cached paths with PATH order
The -p and -P options can be affected by remembered paths. This comparison starts with hash -r in a subshell instead of assuming every result always comes from the first PATH directory.