Wie finde ich zuverlässig den vollständigen Pfad eines Programms auf dem PFAD?

12

Ich muss den Pfad eines bestimmten Programms über die PATHVerwendung eines Shell-Skripts finden. Der Pfad muss der tatsächliche vollständige Pfad des Programms sein, der später an eine der exec*Funktionen übergeben werden kann, die das Programm selbst nicht durchsucht PATH, z execv.

Es gibt Programme wie kill, die als aktuelles Programm und gleichzeitig als eingebaute Shell zur Verfügung stehen. In diesem Fall benötige ich den vollständigen Pfad zum eigentlichen Programm.

PATHWie in Abschnitt 2.9.1.1, Befehlssuche und Ausführung des POSIX-Standards beschrieben, gibt es mehrere Dienstprogramme, die ein Programm auf der finden können .

Es gibt which, was zu keinem Standard gehört. Bei einigen Systemen kann es sich um ein reguläres Programm handeln, bei einigen Shells um ein eingebautes Programm. Es scheint auf den meisten Systemen und Shells verfügbar zu sein, aber die Shells mit einer integrierten Version geben statt des Pfads zur ausführbaren Datei nur den Namen der integrierten Version zurück. Es ist auch in keiner Weise standardisiert und kann Ausgaben zurückgeben und verschiedene Optionen annehmen.

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

Es gibt whence, die ein paar Muscheln eingebaut ist. Bei vielen Shells jedoch nicht verfügbar. Es wird auch der Name des eingebauten anstelle des Pfades zum Programm zurückgegeben. A -pkann an wherece übergeben werden, um dieses Verhalten zu ändern.

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

Es gibt das commandvon POSIX spezifizierte Builtin: 2008 . Leider sucht es auch nach regulären Befehlen und eingebauten Funktionen und gibt den Namen der eingebauten Funktion anstelle des Pfads zu dem Programm zurück, der von einer eingebauten Funktion mit dem gleichen Namen beschattet wird. Einige alte Shells haben es noch nicht implementiert.

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
quelle
Ich kann nicht herausfinden, ob enablein POSIX angegeben ist oder nicht, aber wenn ja, können Sie enable -n whichdie integrierte Shell für deaktivieren which.
Muzer
und es gibtrealpath
Ipor Sircer
@Muzer Über die Muscheln, die ich zu meiner Verfügung habe, enablewird nur von bashundzsh
Sebastian Schrader
1
Sie benötigen eine realisierbare Methode für die spezifische Shell, die Ihr Skript nicht für alle Shells ausführt . Skripte werden nicht von einer zufälligen Shell ausgeführt, sondern speziell von der in der shebang-Zeile angegebenen Shell. Davon abgesehen wäre das in Bash type -p. Sowohl bash als auch dash lassen Sie commandbefehlen, eine aktuelle ausführbare Datei auszuführen, selbst wenn es eine Funktion oder ein eingebautes Programm mit demselben Namen gibt.
AlexP
1
@AlexP commandüberspringt Funktionen (und Aliase), aber KEINE eingebauten Funktionen, wie im Q korrekt angegeben. Und Sie können nicht immer einen Shebang verwenden, da es auf allen Systemen keinen Pfad gibt, der eine bestimmte Shell oder sogar eine POSIX-Shell abruft.
Dave_thompson_085

Antworten:

11

Suchen Sie einfach selbst danach.

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

Geprüft bash, dash, ksh, mksh,zsh

Aktualisieren

Das Obige ist für ein eigenständiges Skript hilfreich. Wenn Sie dies jedoch in ein größeres Skript einbetten möchten, möchten Sie möglicherweise etwas Ähnliches wie das Folgende verwenden.

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
}

Dies ist so, dass IFSnach dem Auffinden der Übereinstimmung auch exit's mit return' s getauscht wird

Zachary Brady
quelle
1
Vielleicht -x statt -f?
Jeff Schaller
@ JeffSchaller guter Punkt, kein Grund, nicht ausführbare Dateien auszuwählen.
Zachary Brady
3
Wenn Sie dies in ein größeres Shell-Skript integrieren (anstatt es, wie vermutet, in ein eigenständiges Skript umzuwandeln), möchten Sie möglicherweise den alten Wert von IFS nachträglich wiederherstellen - andernfalls könnte dies viele Auswirkungen auf haben Der Rest des Drehbuchs ...
Psmears
Warum die IFSVariable exportieren ? Ist es nicht genug, dieses Set in der lokalen Shell zu haben? Apropos lokal, wäre local IFSportabel? Das Obige kann schlecht interagieren, wenn etwas anderes IFS auf die gleiche Weise speichert. Wenn Sie sich diese Frage zu SE ansehen , localfunktioniert sie möglicherweise für die meisten Shells, obwohl sie nicht POSIX sind. Das Einfügen der Originalversion in eine (…)Subshell kann ebenfalls funktionieren.
MVG