$ which {which,type,command,vi}
which: shell built-in command
type: shell built-in command
command: shell built-in command
/usr/bin/vi
$ type {which,type,command,vi}
which is a shell builtin
type is a shell builtin
command is a shell builtin
vi is /usr/bin/vi
$ command -v {which,type,command,vi}
which
type
command
/usr/bin/vi
Which is quite different from how bash 5.1 behaves:
bash-5.1$ which {which,type,command,vi}
/usr/bin/which
/usr/bin/type
/usr/bin/command
/usr/bin/vi
bash-5.1$ type {which,type,command,vi}
which is hashed (/usr/bin/which)
type is a shell builtin
command is a shell builtin
vi is /usr/bin/vi
bash-5.1$ command -v {which,type,command,vi}
/usr/bin/which
type
command
/usr/bin/vi
I wouldn't want to be the one responsible for making this change, that's for sure.
command -v does seem like the most reasonable choice in both circumstances, though.
/usr/bin/type and /usr/bin/command are indeed just the same shell scripts.
file /usr/bin/{which,type,command}
/usr/bin/which: Mach-O universal binary [...]
/usr/bin/type: POSIX shell script text executable, ASCII text
/usr/bin/command: POSIX shell script text executable, ASCII text
cat /usr/bin/command
#!/bin/sh
# $FreeBSD: src/usr.bin/alias/generic.sh,v 1.2 2005/10/24 22:32:19 cperciva Exp $
# This file is in the public domain.
builtin `echo ${0##*/} | tr \[:upper:] \[:lower:]` ${1+"$@"}
Then that's too bad for anyone who tries to use non-standard shells. It's one thing to violate a vague, rarely used standard. It's quite another to violate what is most likely the most broadly supported standard on *nix systems.
Dash is Debian's default non-interactive shell, and it isn't POSIX compliant. The only way to get POSIX compliance on Debian is to switch to a different shell (e.g. bash).
Plus shells also behave different depending on how it's invoked (e.g. as "sh" rather than "zsh" or "bash") which can affect the output of these builtins.
My own preference these days is to explicitly use /bin/zsh and just rely on zsh behaviour (e.g. "$+commands[ls]"). It's not "compatible" in the sense of "POSIX compatible", but it's more compatible in the sense of "much higher chance everything will work on a random system", with the only downside that people will need to install zsh. I think that's a fair trade-off for many (though obviously not all) cases.
The article points out that "command -v" is the only thing in this mess that's actually standardized by POSIX as "print out the path to the thing specified".
The standard[1] is for a shell built-in, not a separate executable:
> Since command is a regular built-in utility it is always found prior to the PATH search.
That's why it's weird that macOS has an executable which wraps it from `sh`, which I've discovered since the GP is something it inherits from FreeBSD.
I'm not sure if FreeBSD still does this (some FreeBSD poster let me know), but apparently they used to generate wrappers for shell builtins pegged to #!/bin/sh and drop them in /usr/bin, presumably so that scripts running in non-POSIX shells that didn't implement them at all could still invoke them. It seems kinda neat. But it also leads to weird behavior where you can run `command` in tcsh and it will tell you something is a shell built-in even though it doesn't appear in the list of tcsh's shell builtins and you're running tcsh, which seems... wrong.
Yeah, I discovered that after I made my previous comment, and before I saw your whole long one.
It's a weird choice, because it means that command will sometimes tell you about 'builtins' that are not actually built in to the shell you invoke it from, including `command` itself.
What Linux distros do? The comments in the `command` script on macOS indicate that FreeBSD included a separate executable for `command` in 2005. But there's no package in all of Nixpkgs providing a `command` executable and the command-not-found handler for Ubuntu doesn't suggest anything either.
The norm, afaict, is for `command` to be a shell built-in only, and not available as a separate program.
command -v does seem like the most reasonable choice in both circumstances, though.
/usr/bin/type and /usr/bin/command are indeed just the same shell scripts.