HN2new | past | comments | ask | show | jobs | submitlogin

Here's how zsh 5.8 behaves on macOS:

    $ 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+"$@"}


"command -v" isn't reliable because several shells (including Dash) don't obey the POSIX standard. See https://github.com/oilshell/oil/blob/8fbc09bb3254cee944b0450...


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).


I checked your link but I don't quite get it. Is status=0 supposed to be a check rather than an assignment?

Either way, it doesn't have that behavior like that for me on Debian: it behaves the same as bash. This is in dash:

    $ command -v whoami
    /usr/bin/whoami
    $ echo $?
    0
    $ command -v echo
    echo
    $ echo $?
    0
    $ command -v nonexistent
    $ echo $?
    127
So that is with both a proper command (whoami), a shell built-in (echo), and neither (nonexistent). It's all as I would expect from a shell.

Bash does exactly the same, although dash chooses 127 as exit status and bash chooses 1 but they're both nonzero (thus error statuses).


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.


As was pointed in many other places, you should also check the output when an alias is used.


Wait what

macOS has an executable called `command`? Does any other OS?


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.

1: https://pubs.opengroup.org/onlinepubs/007904975/utilities/co...


It’s a shell built-in.


No, macOS has an executable called `command` so that it is available even on shells that don't implement the builtin


If you look at the last part of my comment, you’ll see it’s just a shell script that invokes the built-in.


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.


MS-DOS


BSD and Linux do.


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.




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

Search: