Jak niezawodnie znaleźć pełną ścieżkę programu na PATH?

12

Muszę znaleźć ścieżkę do danego programu PATHza pomocą skryptu powłoki. Ścieżka musi być faktyczną pełną ścieżką programu, którą można później przekazać do jednej z exec*funkcji, która nie przeszukuje PATHsamej, np execv.

Istnieją takie programy kill, które są dostępne jako rzeczywisty program i jednocześnie wbudowana powłoka. W takim przypadku potrzebuję pełnej ścieżki do rzeczywistego programu.

Istnieje kilka narzędzi, które mogą znaleźć program w PATHsposób określony w Sekcji 2.9.1.1, Wyszukiwanie poleceń i wykonywanie standardu POSIX .

Istnieje which, który nie jest częścią żadnego standardu. Może być zwykłym programem w niektórych systemach, podczas gdy niektóre powłoki zapewniają, że jest wbudowany. Wydaje się, że jest dostępny w większości systemów i powłok, ale powłoki z wbudowaną wersją również zwracają nazwę wbudowanej zamiast ścieżki do pliku wykonywalnego. Ponadto nie jest w żaden sposób ustandaryzowany i może zwracać dane wyjściowe i przyjmować różne opcje.

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

Jest whence, który jest wbudowany w kilka powłok. Ale niedostępne w wielu powłokach. Zwróci również nazwę wbudowanego zamiast ścieżki do programu. A -pmoże zostać przekazane skąd zmienić to zachowanie.

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

Jest commandwbudowane określone przez POSIX: 2008 . Niestety wyszukuje również zwykłe polecenia i wbudowane funkcje i zwraca nazwę wbudowanego zamiast ścieżki do programu, ukrytą za wbudowanym o tej samej nazwie. Niektóre stare powłoki jeszcze tego nie zaimplementowały.

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
Sebastian Schrader
źródło
Nie mogę ustalić, czy enablejest to określone w POSIX, czy nie, ale jeśli tak, możesz użyć enable -n whichdo wyłączenia wbudowanej powłoki which.
Muzer
i jestrealpath
Ipor Sircer
@Muzer O muszlach, które mam do dyspozycji, enableudzielają tylko bashizsh
Sebastian Schrader
1
Potrzebujesz niezawodnej metody dla konkretnej powłoki, która uruchamia skrypt nie dla wszystkich powłok. Skrypty nie są wykonywane przez losową powłokę, ale konkretnie przez powłokę określoną w linii shebang. Biorąc to pod uwagę, to byłoby bash type -p. Zarówno bash, jak i dash pozwalają wypowiedzieć commandpolecenie uruchomienia rzeczywistego pliku wykonywalnego, nawet jeśli istnieje funkcja lub wbudowana funkcja o tej samej nazwie.
AlexP
1
@AlexP commandpomija funkcje (i aliasy), ale NIE jest wbudowane, jak poprawnie mówi Q. I nie zawsze możesz użyć shebang, ponieważ nie ma ścieżki, która by dała jakąkolwiek powłokę, a nawet powłokę POSIX, we wszystkich systemach.
dave_thompson_085

Odpowiedzi:

11

Poszukaj go sam.

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

Testowany w bash, dash, ksh, mksh,zsh

Aktualizacja

Powyższe jest dobre dla samodzielnego skryptu, jednak jeśli planujesz osadzić go w większym skrypcie, możesz użyć czegoś podobnego do następującego.

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
}

Jest to IFSprzywracane po znalezieniu dopasowania, również zamieniane na exit„s return

Zachary Brady
źródło
1
Może -x zamiast -f?
Jeff Schaller
@JeffSchaller dobry punkt, nie ma powodu, aby wybierać pliki niewykonywalne.
Zachary Brady
3
Jeśli zintegrujesz to z większym skryptem powłoki (w przeciwieństwie do samodzielnego tworzenia skryptu, jak się przypuszcza), możesz później przywrócić starą wartość IFS - w przeciwnym razie może to mieć duży wpływ na reszta skryptu ...
psmears,
Po co eksportować IFSzmienną? Czy nie wystarczy mieć ten zestaw w lokalnej powłoce? Mówiąc o lokalnym, byłby local IFSprzenośny? Powyższe może źle oddziaływać, jeśli coś innego oszczędza IFS w ten sam sposób. Patrząc na to pytanie w SE , localmoże działać dla większości powłok, mimo że nie jest POSIX. Umieszczenie oryginalnej wersji w (…)podpowłoce również może działać.
MvG