Як я надійно знаходжу повний шлях програми на 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, яка є вбудованою з кількох оболонок. Але недоступний на багатьох оболонках. Це занадто поверне ім'я вбудованого замість шляху до програми. А -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
Іпор Сірсер

@ Muzer Про снаряди, які я маю в своєму розпорядженні, enableнадає лише bashтаzsh
Себастьян Шрадер

2
Вам потрібен надійний метод для конкретної оболонки, на якій працює ваш сценарій, не для всіх оболонок. Сценарії виконуються не випадковою оболонкою, а конкретно оболонкою, вказаною в рядку shebang. Це, як сказано, в основному це було б 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}" ] && return 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' s


1
Можливо -x замість -f?
Jeff Schaller

@JeffSchaller хороший момент, немає причин вибирати не виконувані файли.
Захарій Брейді

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

Навіщо експортувати IFSзмінну? Чи не достатньо, щоб цей набір був у локальній оболонці? Якщо говорити про місцеві, це було local IFSб портативним? Вищезазначене може погано взаємодіяти, якщо щось інше економить IFS таким же чином. Дивлячись на це питання на SE , він localможе працювати для більшості оболонок, не маючи POSIX. Поміщення оригінальної версії в (…)допоміжну оболонку також може працювати.
MvG
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.