Как мне надежно найти полный путь к программе в PATH?


12

Мне нужно найти путь к данной программе с PATHпомощью сценария оболочки. Путь должен быть фактическим полным путем программы, который может быть передан позже одной из exec*функций, которая не ищет PATHсаму себя, например execv.

Существуют такие программы, как kill, которые доступны как фактическая программа, так и встроенная оболочка одновременно. Если это так, мне нужен полный путь к самой программе.

Существует несколько утилит, которые могут найти программу, PATHкак указано в разделе 2.9.1.1, Поиск команд и выполнение стандарта POSIX .

Существует which, что не является частью какого-либо стандарта. В некоторых системах это может быть обычная программа, в то время как некоторые оболочки предоставляют ее как встроенную. Кажется, он доступен в большинстве систем и оболочек, но оболочки со встроенной версией также просто возвращают имя встроенной, а не путь к исполняемому файлу. Кроме того, он никак не стандартизирован и может возвращать любой вывод и принимать разные варианты.

bash# which kill
/usr/bin/kill
dash# which kill
/usr/bin/kill
fish# which kill
/usr/bin/kill
mksh# which kill
/usr/bin/kill
tcsh# which kill
kill: shell built-in command.
zsh# which kill
kill: shell built-in command

Существует whence, который является встроенным из нескольких оболочек. Но не доступно на многих снарядах. Он также вернет имя встроенного вместо пути к программе. A -pможет быть передан, чтобы изменить это поведение.

bash# whence kill
bash: whence: command not found
dash# whence kill
dash: 1: whence: not found
fish# whence kill
fish: Unknown command 'whence'
mksh# whence kill
kill
mksh# whence -p kill
/usr/bin/kill
tcsh# whence kill
whence: Command not found.
zsh# whence kill
kill
zsh# whence -p kill
/usr/bin/kill

Существует commandвстроенная функция, указанная в POSIX: 2008 . К сожалению, он также ищет обычные команды и встроенные модули и будет возвращать имя встроенной программы вместо пути к программе, за которой скрывается встроенная команда с тем же именем. Некоторые старые оболочки еще не реализовали это.

bash# command -v kill
kill
dash# command -v kill
kill
fish# command -v kill
/usr/bin/kill
mksh# command -v kill
kill
tcsh# command -v kill
command: Command not found.
zsh# command -v kill
kill

Я не могу понять, enableуказано ли это в POSIX или нет, но если это так, вы можете использовать enable -n whichдля отключения встроенной оболочки для which.
Музер

и естьrealpath
Ipor Sircer

@Muzer На снарядах, которые у меня есть в наличии, enableпредоставлены только bashиzsh
Себастьян Шрейдер

1
Вам нужен надежный метод для конкретной оболочки, которая запускает ваш скрипт не для всех оболочек. Скрипты выполняются не случайной оболочкой, а конкретно оболочкой, указанной в строке shebang. Как говорится, в Bash это будет type -p. И bash, и dash позволяют вам сказать commandкоманду для запуска реального исполняемого файла, даже если есть функция или встроенная функция с тем же именем.
AlexP

1
@AlexP commandпропускает функции (и псевдонимы), но НЕ встроенные, как правильно говорит Q. И вы не всегда можете использовать шебанг, потому что нет пути, по которому можно получить какую-либо оболочку или даже оболочку POSIX во всех системах.
dave_thompson_085

Ответы:


11

Просто найдите это сами.

export IFS=":"
[ -z "${1}" ] && exit 1
for dir in $PATH
do if [ -x "${dir}/${1}" ]
   then echo "${dir}/${1}"
        exit 0
   fi
done
echo ${1} not found
exit 1

Испытано в bash, dash, ksh, mksh,zsh

Обновить

Вышеприведенное хорошо для отдельного скрипта, однако, если вы планируете встроить его в более крупный скрипт, вы можете использовать что-то более похожее на следующее.

function find_path() {
   IFS_SAVE="${IFS}"
   export IFS=":"
   [ -z "${1}" ] && exit 1
   for dir in $PATH
   do if [ -x "${dir}/${1}" ]
      then echo "${dir}/${1}"
           export IFS="${IFS_SAVE}"
           return 0
      fi
   done
   export IFS="${IFS_SAVE}"
   echo ${1} not found
   return 1
}

Это объясняется тем , что IFSвосстанавливается после обнаружения матча, а также поменять местами exit«s с return» ы


1
Может быть, -x вместо -f?
Джефф Шаллер

@JeffSchaller Хороший вопрос, нет причин выбирать неисполняемые файлы.
Захари Брэди

3
Если вы интегрируете это в больший сценарий оболочки (в отличие от самостоятельного превращения его в сценарий, как предполагается), вы можете захотеть впоследствии восстановить старое значение IFS - в противном случае это может оказать большое влияние на остальная часть сценария ...
psmears

Зачем экспортировать IFSпеременную? Разве не достаточно иметь этот набор в локальной оболочке? Говоря о местных, будет local IFSпортативным? Вышесказанное может плохо взаимодействовать, если что-то еще сохраняет IFS таким же образом. Глядя на этот вопрос на SE , localможет работать для большинства оболочек, несмотря на то, что не POSIX. Помещение оригинальной версии в (…)подоболочку может также работать.
MvG
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.