Узнайте, существует ли команда в системе POSIX

Я хочу иметь возможность определить, существует ли команда в любой системе POSIX из сценария оболочки.

В Linux я могу делать следующее:

if which <command>; then
   ...snip...
fi

Однако Solaris и MacOS which не выдают код ошибки выхода, когда команда не существует, они просто выводят сообщение об ошибке в STDOUT.

Кроме того, недавно я обнаружил, что сама команда which не является POSIX (см. http://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html)

Любые идеи?


person singpolyma    schedule 18.04.2009    source источник


Ответы (3)


command -v - это команда, указанная в POSIX, которая выполняет то, что делает.

Определено, чтобы возвращать> 0, когда команда не найдена или возникает ошибка.

person singpolyma    schedule 18.04.2009
comment
Я на 100% согласен с использованием command -v, я использую его постоянно, чтобы проверить доступность команд. Например, command -v CMD > /dev/null || echo 'CMD not found' Никаких глупых ifs не требуется !! - person J. M. Becker; 13.08.2012
comment
if не глуп только потому, что это if, это было глупо в этом случае использования, поскольку это было посторонним. Не было группировки и, следовательно, дополнительной семантической ценности, это была бы просто ненужная переоценка. Разве это может быть оправдано абсолютно! Фанатик любой практики может оправдать почти все, и в этом случае последствия незначительны ... но это не значит, что они верны или что все равно. Обычно я делаю это так: одна команда (включая тест) с одним кодом ошибки, без оператора if. Как только вы получите больше, тогда if может быть более чем разумным. - person J. M. Becker; 11.03.2015
comment
Оболочка POSIX не требуется для реализации опции command -v. См. stackoverflow.com/q/34572700/1175080. - person Lone Learner; 03.01.2016
comment
Нашел это самое использование в git-хуке, созданном git-lfs command -v git-lfs >/dev/null 2>&1 || { echo >&2 "git-lfs command not found"} (сообщение об ошибке более подробное, но дело не в этом) - person Marcello Romani; 12.04.2017
comment
@LoneLearner в последней версии спецификации обязателен. - person jarno; 29.04.2021
comment
command -v не выполняет в точности то, что делает which: последний завершается с ненулевым кодом, если аргумент является псевдонимом или функцией оболочки. - person jarno; 29.04.2021

Вы можете прочитать stdout / stderr of "which" в переменной или массиве (используя обратные кавычки), а не проверять код выхода.

Если в системе нет команд «который» или «где», вы также можете получить содержимое переменной $ PATH, затем перебрать все каталоги и найти данный исполняемый файл. По сути, это то, что делает (хотя может потребоваться некоторое кеширование / оптимизация результатов $ PATH).

person Andy White    schedule 18.04.2009
comment
Проблема в том, что даже в системах, в которых есть что, я не гарантирую, будет даже какой-либо результат ... Думаю, я мог бы просто проверить, чтобы убедиться, что это не так. не похоже на путь ... - person singpolyma; 18.04.2009

function_command_exists для проверки наличия команды:

#!/bin/sh

set -eu

function_command_exists() {
    local command="$1"
    local IFS=":" # paths are delimited with a colon in $PATH

    # iterate over dir paths having executables
    for search_dir in $PATH
    do
        # seek only in dir (excluding subdirs) for a file with an exact (case sensitive) name
        found_path="$(find "$search_dir" -maxdepth 1 -name "$command" -type f 2>/dev/null)"

        # (positive) if a path to a command was found and it was executable
        test -n "$found_path" && \
        test -x "$found_path" && \
            return 0
    done
    
    # (negative) if a path to an executable of a command was not found
    return 1
}

# example usage
echo "example 1";

command="ls"
if function_command_exists "$command"; then
    echo "Command: "\'$command\'" exists"
else
    echo "Command: "\'$command\'" does not exist"
fi

command="notpresent"
if function_command_exists "$command"; then
    echo "Command: "\'$command\'" exists"
else
    echo "Command: "\'$command\'" does not exist"
fi

echo "example 2";

command="ls"
function_command_exists "$command" && echo "Command: "\'$command\'" exists"

command="notpresent"
function_command_exists "$command" && echo "Command: "\'$command\'" does not exist"

echo "End of the script"

вывод:

example 1
Command: 'ls' exists
Command: 'notpresent' does not exist
example 2
Command: 'ls' exists
End of the script

Обратите внимание, что даже использовалась опция set -eu, которая включает -e для сценария, сценарий был выполнен до последней строки. Конец сценария.

В example 2 нет Command: 'notpresent' does not exist из-за оператора &&, поэтому выполнение echo "Command: "\'$command\'" does not exist" пропускается, но выполнение сценария продолжается до конца.

Обратите внимание, что function_command_exists не проверяет, имеете ли вы право выполнять команду. Это нужно делать отдельно.

Решение с command -v <command-to-check>

#!/bin/sh
set -eu;

# check if a command exists (Yes)
command -v echo > /dev/null && status="$?" || status="$?"
if [ "${status}" = 127 ]; then
   echo "<handle not found 1>"
fi

# check if a command exists (No)
command -v command-that-does-not-exists > /dev/null && status="$?" || status="$?"
if [ "${status}" = 127 ]; then
   echo "<handle not found 2>"
fi

производит:

<handle not found 2>

потому что echo был найден в первом примере.

Решение с запуском command и обработкой ошибок, включая не найденную команду.

#!/bin/sh

set -eu;

# check if a command exists (No)
command -v command-that-does-not-exist > /dev/null && status="$?" || status="$?"
if [ "${status}" = 127 ]; then
   echo "<handle not found 2>"
fi

# run command and handle errors (no problem expected, echo exist)
echo "three" && status="$?" || status="$?"
if [ "${status}" = 127 ]; then
   echo "<handle not found 3>"

elif [ "${status}" -ne 0 ]; then
   echo "<handle other error 3>"
fi

# run command and handle errors (<handle not found 4> expected)
command-that-does-not-exist && status="$?" || status="$?"
if [ "${status}" = 127 ]; then
   echo "<handle not found 4>"

elif [ "${status}" -ne 0 ]; then
   echo "<handle other error 4>"
fi

# run command and handle errors (command exists but <handle other error 5> expected)
ls non-existing-path && status="$?" || status="$?"
if [ "${status}" = 127 ]; then
   echo "<handle not found 5>"

elif [ "${status}" -ne 0 ]; then
   echo "<handle other error 5>"
fi

производит:

<handle not found 2>
three
./function_command_exists.sh: 34: ./function_command_exists.sh: command-that-does-not-exist: not found
<handle not found 4>
ls: cannot access 'non-existing-path': No such file or directory
<handle other error 5>
person Jimmix    schedule 10.01.2021