Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

'command -v' is POSIX!! I have always avoided it in favor of which because its output doesn't feel machine friendly.


I learned about `command -v` just now reading this article. I only knew of `which` and `type` (as well as the very convenient `=executable` syntax that expands to the path of the binary, but I think that's a zsh-ism).

What makes `command -v` not machine friendly? It seems to always output just the PATH, unlike type which tries to be human friendly.


It's pretty much useless for the same purpose:

    $ which ls
    /usr/bin/ls
    $ type ls
    ls is aliased to `ls --color=auto'
    $ command -v ls
    alias ls='ls --color=auto'
Suddenly I find myself irritated at Debian despite not even using it. Why would they not just include the GNU one?


That depends on whether your shell has a built-in which or not. Mine says

    % which ls
    ls: aliased to /bin/ls --color=auto
Which makes way more sense. Your "which" is not telling you what will actually be executed when you run the `ls` command there. `command`, on the other hand, is guaranteed to be a built-in, has consistent behavior, and has defined, consistent output, unlike `which`. What `which` outputs will be different depending on shell and what implementation of `which` you actually have installed.


That depends on which which you mean. Which on GNU has always been this:

    NAME
       which - shows the full path of (shell) commands.
    DESCRIPTION
       Which takes one or more arguments. For each of its arguments it prints to stdout the full path of the
       executables that would have been executed when this argument had been entered at the shell prompt. It
       does this by searching for an executable or script in the directories listed in the environment vari‐
       able PATH using the same algorithm as bash(1).


Unless you're using a shell that subverts that by providing its own built-in, like Zsh, which is allowed because there is no standard for `which`, and depending on its behavior can be problematic and inherently non-portable.

That description is also doesn't correctly describe the behavior of the command if the shell has any aliases or built-ins of that name. If you have an alias that points to a different command, then `which` is distinctly not printing the executable that would have been executed.


> That description is also doesn't correctly describe the behavior of the command if the shell has any aliases or built-ins of that name. If you have an alias that points to a different command, then `which` is distinctly not printing the executable that would have been executed.

I have some bad news.

    % command -v which
    Unknown option: v
    _______
    < which >
    -------
            \   ^__^
             \  (oo)\_______
                (__)\       )\/\
                    ||----w |
                    ||     ||
    % alias
    command=cowsay
    run-help=man
    which-command=whence
    %
Interestingly, at least one shell will not let you do this, but it's not entirely POSIX compliant anyway:

    [I] ⋊> ~ echo $FISH_VERSION
    3.3.1
    [I] ⋊> ~ alias command cowsay
    - (line 1): function: The name 'command' is reserved, and cannot be used as a function name
    function command --wraps cowsay --description 'alias command cowsay';  cowsay $argv; end
    ^
    from sourcing file -
            called on line 70 of file /nix/store/gwc21f4ra55h0x0b8xbwnpjlc6223z3q-fish-3.3.1/share/fish/functions/alias.fish
    in function 'alias' with arguments 'command cowsay'
It's also probably worth noting at this point that portability isn't the same kind of issue for interactive shells as it is for scripts, and you should probably not expect to be using or encountering aliases in scripts at all, if you can avoid it.


As I said, it depends on which which you mean. In this case, Debian is changing it's behavior away from how it's been for 28 years or whatever and breaking heaps of code and people's habits in the process.


Yes, you're right. I think it's fine to depend on the existing which behavior for the current use cases. I just disagree that the behavior is really more useful than `command -v` for any sort of build or scripting purposes. It's definitely more useful as a user-facing utility to have a very recognizable name, like `which`. I'm one of the people who had never heard of `command -v` before now, and I'd used `which` for scripting, because I assumed it was standardized. I just don't see much use case for a shell command that finds a command in the path while specifically ignoring all aliases, functions, and built-ins over something like `command -v`.

> In this case, Debian is changing it's behavior away from how it's been for 28 years

In this case, Debian has voted to keep it the same: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=994275


> I just don't see much use case for a shell command that finds a command in the path while specifically ignoring all aliases, functions, and built-ins over something like `command -v`.

It's useful if you want to track down an executable! For example, if you're performing an Arch Linux install for the first time in years, and you notice that a program is missing on your install but present on the installation media, you can use `which` in combination with `realpath` and `pacman -Qf` to find out what you need to install to get it. (This happened to me a couple days ago, when I decided to really revisit Arch for the first time in over a decade.)

Also, `command -v` reporting builtins is kinda against the spirit of the `command` command in the first place. From, for example, the bash help:

    $ 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.
> Runs COMMAND with ARGS suppressing shell function lookup

Or in the ksh manual:

       command [ -pvxV ] name [ arg ... ]
              Without  the  -v  or  -V options, command executes name with the arguments given by arg.  The -p option causes a default path to be searched rather than the one defined by the value of PATH.  Functions will not be searched for when finding name.  In addition, if name refers to a special built-in, none of the special properties associated with the leading daggers will be honored.  (For example, the predefined alias redirect=′command exec′ prevents a  script  from  terminating when  an invalid redirection is given.)  With the -x option, if command execution would result in a failure because there are too many arguments, errno E2BIG, the shell will invoke command name multiple times with a subset of the arguments on each invocation.  Arguments that occur prior to the first word that expands to multiple arguments and after the last word that expands to multiple arguments will be passed on each invocation.  The exit status will be the maximum invocation exit status.  With the -v option, command is equivalent to the built-in whence command described below.  The -V option causes command to act like whence -v.
              
> Functions will not be searched for when finding name. In addition, if name refers to a special built-in, none of the special properties associated with the leading daggers will be honored.

and so on.


You can use the `command` command to determine which `which` you use ;)

    % which which
    which: shell built-in command
    % command which which
    /run/current-system/sw/bin/which
I like to use `which` together with `realpath` to see what exact version of a program I'm using, e.g. (in Fish),

    [I] ⋊> ~ realpath (command which which)
    /nix/store/3w3rvxhlv5dcmdih72da6m613qyav9kw-which-2.21/bin/which
Idk if it's also POSIX, but `command` also typically has an analogue called `builtin` that you can use ensure that you are not looking at an external command, e.g.:

    [I] ⋊> ~ builtin which which   # Fish doesn't have a builtin called `which`
    fish: Unknown builtin “which”
    [I] ⋊> ~ command command command # and I don't have an external command called `command`
    command: command not found
It can also be useful for figuring out which package owns an executable you're running, e.g., on Debian-based systems (also with Fish):

    > apt show (dpkg -S (realpath (command which php)) | cut -d':' -f1)
    Package: php7.4-cli
    Version: 7.4.25-1+ubuntu18.04.1+deb.sury.org+1
    Priority: optional
    Section: php
    Source: php7.4
    Maintainer: Debian PHP Maintainers <team+pkg-php@tracker.debian.org>
    Installed-Size: 4,711 kB
    Provides: php-cli, phpapi-20190902
    Depends: libedit2 (>= 2.11-20080614-4), libmagic1, mime-support, php7.4-common (= 7.4.25-1+ubuntu18.04.1+deb.sury.org+1), php7.4-json, php7.4-opcache, php7.4-readline, tzdata, ucf, libargon2-1 (>= 0~20171227), libc6 (>= 2.27), libpcre2-8-0 (>= 10.32), libsodium23 (>= 1.0.14), libssl1.1 (>= 1.1.0), libxml2 (>= 2.8.0), zlib1g (>= 1:1.1.4)
    Suggests: php-pear
    Download-Size: 1,398 kB
    APT-Sources: http://ppa.launchpad.net/ondrej/php/ubuntu bionic/main amd64 Packages
    Description: command-line interpreter for the PHP scripting language
    This package provides the /usr/bin/php7.4 command interpreter, useful for
    testing PHP scripts from a shell or performing general shell scripting tasks.
    .
    The following extensions are built in: Core date filter hash libxml openssl
    pcntl pcre Reflection session sodium SPL standard zlib.
    .
    PHP (recursive acronym for PHP: Hypertext Preprocessor) is a widely-used
    open source general-purpose scripting language that is especially suited
    for web development and can be embedded into HTML.

    N: There is 1 additional record. Please use the '-a' switch to see it
> What `which` outputs will be different depending on shell

This is also true of `command -v`, whose behavior with respect to builtins varies per shell, and is not implemented in some shells.

Fish:

    [I] ⋊> ~ fish --version                                                                                                  10:55:42
    fish, version 3.3.1
    [I] ⋊> ~ command -v command
    [I] ⋊> ~ command -v which
    /run/current-system/sw/bin/which
tcsh:

    > tcsh --version
    tcsh 6.22.04 (Astron) 2021-04-26 (x86_64-unknown-linux) options wide,nls,dl,al,kan,sm,rh,color,filec
    > command -v command
    command: Command not found.
    > command -v which
    command: Command not found.
    > builtins | grep command
    >

ksh:

    $ ksh --version
      version         sh (AT&T Research) 2020.0.0
    $ command -v command
    'command '
    $ command -v which
    /run/current-system/sw/bin/which
    $
mksh:

    $ echo $KSH_VERSION
    @(#)MIRBSD KSH R59 2020/10/31
    $ command -v command
    command
    $ command -v which
    /run/current-system/sw/bin/which
    $
pwsh:

    PS> $PSVersionTable

    Name                           Value
    ----                           -----
    PSVersion                      7.1.4
    PSEdition                      Core
    GitCommitId                    7.1.4
    OS                             Linux 5.14.12 #1-NixOS SMP Wed Oct 13 07:42:04 UTC 2021
    Platform                       Unix
    PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
    PSRemotingProtocolVersion      2.3
    SerializationVersion           1.1.0.1
    WSManStackVersion              3.0

    PS> command -v command
    PS> command -v which
    PS> command which

    CommandType     Name                                               Version    Source
    -----------     ----                                               -------    ------
    Application     which                                              0.0.0.0    /run/current-system/sw/bin/which
Granted, some of those shells (Fish, tcsh, PowerShell) don't aim for full POSIX compliance, and the most popular shells (bash, dash, zsh) all behave the same way as mksh in the example above. But you can also see that the output given for builtins varies among POSIX shell implementations by comparing the output of the AT&T Korn shell to the MirBSD Korn shell.


tcsh on my system:

    $ tcsh --version
    tcsh 6.21.00 (Astron) 2019-05-08 (x86_64-apple-darwin) options wide,nls,dl,bye,al,kan,sm,rh,color,filec
    $ tcsh
    % command -v command
    command
    % command -V command
    command is a shell builtin
    % builtins | grep command
    %


Yeah, this is probably because macOS has done something very, very weird, and added an executable `/usr/bin/command` to the system. These are the contents on the old MacBook I have for work right here:

    #!/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+"$@"}
If you have SIP disabled, try renaming `/usr/bin/command` to `/usr/bin/command.wtf` and see if `tcsh` still acts like there's a `command` command.


I do have SIP disabled for DYLD_LIBRARY_PATH and a handful of other things, but macOS still says /usr/bin/command is on a read-only file system.

[edit]

Not hard to remount root as writable

    $ sudo mount -uw /
    Password:
    $ sudo mv /usr/bin/command /usr/bin/command.save
    $ tcsh
    % command -v command
    command: Command not found.
    % command -V command
    command: Command not found.
    % 
Now reversing the changes

   % exit
   exit
   $ sudo mv /usr/bin/command.save /usr/bin/command
   $ sudo mount -ur /
   mount_apfs: volume could not be mounted: Invalid argument
   mount: / failed with 66
Looks like I'll have to reboot to get the read-only state back.


Someone should make this into a blog post at this point :P


This is a very unlikely output when run from a script using the /bin/sh interpreter on Debian, though.

If that is the output you've gone out of your way to create an alias in a script, in which case it's reasonable output. It is what will happen when the script runs that command, after all.


I'm certain there's masses of code that depends on `which` responding the way it does and scripts with aliases in them regardless of whether that was the right way to do it or not, so your point is probably irrelevant in the grand scheme of things. Think about all that enterprise install and setup crap. People still depend on that spaghetti trash working.


I think aliases are only used in interactive shells:

    $ sh -c 'command -v ls'
    /bin/ls


> I learned about `command -v` just now reading this article

Me too and I'm sure we're not the only ones. But look at this quote from the article:

> surely no one competent would choose to have a package depend on `which` when a standard POSIX utility can do a better job

This is an mind-bogglingly misguided attitude. Anyone that has never heard of an obscure POSIX command is incompetent? Or is it that really that most people don't want to spend their time writing awful shell scripts and figuring out exactly what is POSIX and what isn't?


I'd also never heard of `command -v` until reading this article just now. But then I've only been using *nix systems for about 25 years.


I heard of 'command -v' once before years ago, then completely forgot about it until this article. I don't like it because it's harder to type and harder to remember (why the -v?).

But I've only been using Unix for 37 years, so I'm not really competent.


If you're parsing the output of `command -v`, you're doing it wrong:

  if command -v foo >/dev/null; then
    foo ...
  else
    bar
  fi
From shell scripts it doesn't matter if foo is an alias because if it is an alias, it's one that the script created itself.

For interactive use, just go ahead and use which if that's what you like. Most people will be using bash or z-shell or whatever, and portability isn't a concern.

Interactive shell usage and shell scripting are quite distinct. Yes, there's a huge overlap, but as in any other language, when you're writing a properly structured program (not a one-off or hack), you're expected (for good reasons) to follow more consistent rules and conventions.


"command -v" in Dash (the Debian shell) has a serious bug that makes it non-POSIX compliant: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264


To be honest it's kind of weird to have things in your PATH that are not meant to be executed, so I'm not sure I agree about it being that important and it doesn't seem to have gotten any attention since 2017 despite a patch being available, but I take your point that this should not happen and is of course noncompliant.


What a mess. It sounds like command -v should have been the default from the get go and which should never have been introduced to debianutils. Once it was though, IMO, debian should never make a decision that breaks existing functionality.

In general, I don't care if you extend features beyond POSIX in your core utilities, but once you do, you have to assume people rely on that functionality.

I think this is the most poignant take:

>A proper transition plan would mean that I would never even notice this. One which would replace another and nothing would break. That is the sort of thing I expect to happen in Debian - we are generally good at this stuff, and it's a big reason for using the distro.


I am fairly certain that which came first.

This is the POSIX specification for command:

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/c...


Notable sections from that page:

> The command -v and -V options were added to satisfy requirements from users that are currently accomplished by three different historical utilities: type in the System V shell, whence in the KornShell, and which in the C shell. Since there is no historical agreement on how and what to accomplish here, the POSIX command utility was enhanced and the historical utilities were left unmodified. The C shell which merely conducts a path search. The KornShell whence is more elaborate-in addition to the categories required by POSIX, it also reports on tracked aliases, exported aliases, and undefined functions.

> The output format of -V was left mostly unspecified because human users are its only audience. Applications should not be written to care about this information; they can use the output of -v to differentiate between various types of commands, but the additional information that may be emitted by the more verbose -V is not needed and should not be arbitrarily constrained in its verbosity or localization for application parsing reasons.


Hmm. So you have two conflicting conventions; and the solution is a third convention. Now you have three conflicting conventions.

I think I've come across this somewhere else.


The third convention isn't conflicting with the rest - that's the whole point!


shellcheck throws a lint error if it finds you using which and tells you to use 'command -v' instead.


Thank you for reminding me about this neat tool, it saved my ass a couple of times.


I just tried it on the shellcheck site. It didn't flag it.


I noticed that on Ubuntu at least, `command -v` has the same output format of `which`, where `command -V` does not. Did you accidentally use the latter one?


I'm not sure I knew about type. I didn't know about command -v probably because I learned which close to 35 years ago and I didn't have to look for a different way to do it.


What's not machine-friendly about `command -v`'s output? Can you not just use the exit status? Or did you mean that you avoid `which` in favor of `command -v`?


  $ which true
  /usr/bin/true
  $ command -v true
  true
  $ which ls
  /usr/bin/ls
  $ command -v ls
  alias ls='ls --color=auto'


In this case, `which` is just searching the `PATH` and not telling you what will actually run. `command` is correctly informing you of the whole story. I'll add that `which` on my setup is using the zsh built-in, which also informs of aliases and built-ins.

So yes, that's more useful if you're using `which` to determine "Does this name exist as an executable anywhere in the PATH", but most people use it to mean "What will actually be executed if I run this word as a command?"

edit: Or, most often in scripts, it's used just for its exit status to tell whether the command exists to be executed at all.


> `command` is correctly informing you of the whole story.

You are assuming a bit too much here.

Which tells you the preferred executable with that name, while command tells you what will run if you execute it on the current shell.

From what I can tell, one does not replace the other. But yes, for your example of usage, command is more correct.


In a script, there will almost never be any aliases defined, because noninteractive shells don’t load ~/.bashrc.

    $ sh -c "which ls; command -v ls"
    /bin/ls
    /bin/ls
If the script is #!bash rather than #!sh, it’s not even possible to define an alias.

    $ bash -c "alias ls='ls --color=auto'; which ls; command -v ls"
    /bin/ls
    /bin/ls


You can still define functions in scripts, and those might wrap commands or accidentally share their names.


See my post above.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: