Hacker News new | ask | show | jobs
by kelnos 1695 days ago
> * The POSIX-blessed way of finding an executable program is command -v, which is consequently built into most shells. Given the standard alternative, Adams said, "surely no one competent would choose to have a package depend on `which` when a standard POSIX utility can do a better job".*

This feels a little tone-deaf to me. I've been using the *nix command-line and writing (and reading) shell scripts for 20+ years, and I'd never heard of "command -v" until now. Now that I know about it, I'll probably start trying to retrain my muscle memory to use it (though it requires 2x the number of keystrokes).

Despite its lack of true standardization, "which" (along with "type -p") has been the de-facto "standard" I've seen in shell scripts for figuring out if a command exists. It was a surprise to me to learn that "which" isn't a part of POSIX, even.

6 comments

You should be using ShellCheck or an equivalent linter on your shell scripts in POSIX mode if you want them to be portable; in POSIX mode it warns when using "which" instead of "command -v", and I think it also warns when using "whence" or "where" outside of zsh. There's a host of other Bashisms/GNUisms and other noncompliant antipatterns it checks for too.

It's also worth reading through the POSIX manpages for POSIX sh if you want to be a good shell scripter.

> It's also worth reading through the POSIX manpages for POSIX sh if you want to be a good shell scripter.

Or you can give the middle finger to POSIX, write shell scripts for bash, or even zsh, and end up with faster, more correct, and more beautiful shell scripts that in practice are just as portable as the ones that mindlessly follow the limited and bizarre POSIX shell spec.

I'll write POSIXly-correct shell scripts when the POSIX people stop pretending it's still 1995.

Actually, regarding shells, the POSIX people pretend that it is still 1988.

The POSIX shell is the Bourne shell (1979) plus half of the innovations brought by ksh88.

The other half of the improvements from ksh88 and all of the improvements from ksh93 are not included in the POSIX shell standard.

Also for POSIX compliant shells one must not forget that also the other programs invoked by the shell must not be used with non-POSIX options.

For example, in a bash script I used the ksh93 syntax:

  ${VARIABLE:64:32}
Then I wanted to make it POSIX compliant and I rewrote it in the longer:

  $(expr substr "${VARIABLE}" 65 32)
But then I discovered that POSIX expr lacks substr, so I had to rewrite the expression into the even longer:

  $(awk -v s="${VARIABLE}" 'BEGIN {print substr(s, 65, 32)}')
which is the only POSIX compliant way to extract substrings.

In most cases the rational way is to write scripts for bash or zsh, which include all the ksh93 features plus brace expansion (from csh, then enhanced by zsh).

Writing POSIX compliant scripts results in much longer scripts in which the chances of bugs are much higher.

> Or you can give the middle finger to POSIX

Ok, pleas someone tell me how to use that "upvote" festure on HN :)

More on the subject, nor type nor command, nor constantly mentioned shellcheck recipe for all posixs actually do what which do - just returns path ! Looks like with some options, maybe, but it could be simply added with "which -h" - (standard *nix human readable form toggle), if -h is not already taken.

POSIX in the first place should just standarise which instead of inventing some "command".

Advice: to write best POSIX scripts write moustly-POSIX scripts and be done. Simply becose you should write big shell scripts in the first place. It is Perl job and it do it very better. And if you want more you need C and actually using system calls yourself, eg. for precise path not-globbing.

PS. Now just wait for Mr Poettering take on the subject becouse why be bothered by decades old working admins opinions or work conditions ? Or usable standards, or already working code, or not containers use cases, or just short, human friendly commandline ? Or not tangled and small codebases ?? Let's leave it to, I don't know, IBM ? Is systemd portable to AIX yet, any plans ? ;) WINDOWIZACION95 FTW

You're free to do this, but be aware that you'll then need to be aware of where your scripts will and won't run.

I regularly use Fedora, Alpine Linux, Void, Debian, and OpenBSD. Void uses dash as its default shell, Debian uses it as /bin/sh but still includes bash, OpenBSD uses another POSIXy shell, Fedora uses Bash. macOS also uses zsh as /bin/sh.

If you want portability, you don't have much of a choice. Devs not caring about portability is why less common operating systems are a struggle to use which only fuels monocultures.

Standards compliance and implementation diversity have a symbiotic relationship that is necessary for the health of open platforms. I described the benefits in more detail in a blog post:

https://seirdy.one/2021/02/23/keeping-platforms-open.html

If your shell scripts are complex enough for you to be limited by POSIX then they probably shouldn't be shell scripts in the first place.
My story would echo this same sentiment. Incidentally, running `man command` on OS X actually does not reference a '-v' option at all, instead only stating the more verbose `command which`. Both appear to work, but it further highlights your (our) discoverability issue(s).
There is no man or info page for "command" on Ubuntu, I guess it's just a bash builtin ("which command" doesn't find it). "which" has a man page though.
In bash, such things are documented in the help system:

    $ help command
    command: command [-pVv] command [arg ...]
        Execute a simple command or display information about commands.
    
        Runs COMMAND with ARGS suppressing  shell function lookup, or display
        information about the specified COMMANDs.  Can be used to invoke commands
        on disk when a function with the same name exists.
    
        Options:
          -p    use a default value for PATH that is guaranteed to find all of
                the standard utilities
          -v    print a description of COMMAND similar to the `type' builtin
          -V    print a more verbose description of each COMMAND
    
        Exit Status:
        Returns exit status of COMMAND, or failure if COMMAND is not found.
I didn't know about the "help" command in bash. I've always gone to "man bash", and then scrolled up and down for an hour or so.

Or just cranked-up a search engine.

So thanks for that.

Apparently, there's an "info" command, as well
Though info isn't POSIX compliant.

Not to mention that most distributions completely ignore it and don't populate it with a program's info documentation -- even when such exists.

Wow. I never knew this. I tried scouring through the bash manpage to get hold of its inbuilt commands. Thanks.
For bash, it is documented with 'help command' or the bash man page, section SHELL BUILTIN COMMANDS.
The main problem is there is no good way to provide manpages for shell builtins, like `command`.

Man can only have one entry (per section) for a given command, but what if you're using Bash? Or Zsh? How would it know which one to give you? If they're named differently (like a manpage for `bash-command` vs `zsh-command`), how would you know to look those up? (never mind that apropos would give you so-overloaded-to-be-useless results for something as generic as `command`)

Which leaves us in the current situation where `man command` redirects to a generic `builtin` manpage, that doesn't have much info, and certainly nothing specific to Bash or Zsh.

There could potentially be a bash-<builtin> man page. For example, bash-command or bash-mapfile.
`command which` just executes `which`
Standards are supposed to reflect time-tested practice, not the other way around. Instead of "fixing" every shell script on Earth to use "command -v" instead of the superior and perfectly functional which(1) command, we should just change POSIX and standardize which(1).
`which` and `command -v` give different results when you have aliased a command, e.g., `alias ls='ls --color=auto'`.
I rarely write/read shell scripts, and have been using "command -v" for years.
Once I tried to use `which` in cross-platform scripts, all sorts of issues crept up.

The output and return result differ significantly between BSD, GNU and OSX, so you can't just run $(which python) and get a straightforward response back.

Granted, I didn't know about `command -v` so I'm not sure if it works better in practice.