Stellen Sie ein IFS
, :
um den Wert von PATH
on Doppelpunkten zu teilen . Wenn Sie find
die -quit
Aktion und die -maxdepth
primäre haben (z. B. FreeBSD, OSX, GNU), wissen Sie, dass der Befehl vorhanden ist und Sie sich nicht für den Rückkehrcode des Befehls interessieren, können Sie diesen Einzeiler verwenden:
pattern='libreoffice?.?'
IFS=:; find $PATH -maxdepth 1 -type f -name "$pattern" -exec {} \; -quit; unset IFS
Dies bietet keine einfache Möglichkeit, um zu melden, ob der Befehl gefunden wurde. Um robuster zu sein, deaktivieren Sie außerdem das Globbing, falls der Wert von PATH
Platzhalter enthält. Es ist auch möglich, eine leere Komponente PATH
zu haben, um das aktuelle Verzeichnis zu bezeichnen (aber mein Rat ist, .
stattdessen zu verwenden ). Der folgende Code kümmert sich um all diese Komplikationen.
pattern='libreoffice?.?'
case $PATH in
:*) directories=.$PATH;;
*::*) directories=${PATH%%::*}:.:${PATH#*::};;
*:) directories=$PATH.;;
*) directories=$PATH;;
esac
set -f; IFS=:
cmd=
for d in $directories; do
set +f
for x in "$d"/$pattern; do
if [ -x "$x" ] && ! [ -d "$x" ]; then
cmd=$x
break
fi
done
if [ -n "$cmd" ]; then break; fi
done
set +f; unset IFS
if [ -z "$cmd" ]; then
echo 1>&2 "$pattern: not found in PATH"
exit 127
else
exec "$cmd"
fi
Wenn Sie zufällig zsh verwenden (im Gegensatz zu normalem sh, bash, ksh,…), ist es viel einfacher, eine robuste Lösung zu erstellen.
pattern='libreoffice?.?'
matches=($^path/$~pattern(N.*[1]))
if ((!#matches)); then
$matches[1]
else
echo 1>&2 "$pattern: not found in PATH"
exit 127
fi
Gilles 'SO - hör auf böse zu sein'
quelle
::
es mehrmals vorkommen$PATH
. Möglicherweise möchten Sie den Fall berücksichtigen, in dem$PATH
festgelegt, aber auch leer (durch ersetzen.
).::
davon ausgegangen, dass.
for::
sowieso nur Legacy-konform ist - und neuere Anwendungen sollten eine solche Annahme treffen.find $PATH -type f -perm /u=x,g=x,o=x -prune -name "$@" | head -n1
$PATH
(und ja, das passiert)."$1"
, da es hier nur ein Muster geben kann.Mit
zsh
:In
zsh
,$commands
ist eine spezielle assoziative Array , dessen Schlüssel Befehlsnamen und den Wert ihres Pfades.i
Oben befindet sich ein Array-Index-Flag , das angibtzsh
, dass das Muster mit den Array-Schlüsseln abgeglichen und der erste übereinstimmende Schlüssel zurückgegeben werden soll.Die Elemente des assoziativen Arrays befinden sich jedoch nicht in einer bestimmten Reihenfolge, sodass der erste übereinstimmende Schlüssel nicht unbedingt der erste ist, der in auftritt
$PATH
. Wenn Sie dielibreoffice
mit der größten Versionsnummer möchten , können Sie Folgendes tun:Das
I
tiefgestellte Flag wird auf alle übereinstimmenden Schlüssel erweitert. Wir verwenden die Parametererweiterungsflagsn
(numerische Sortierung) undO
(umgekehrte Reihenfolge) , um diese Liste von der größten zur kleinsten Versionsnummer zu sortieren und dann die erste auszuwählen.[1]
Siehe auch:
um die Pfade der entsprechenden Befehle zu finden.
quelle
$commands[cmd]
ist der erstecmd
in$PATH
, es ist nur so, dass, wenn Sie/bin/libreoffice4.2
und haben/usr/bin/libreoffice4.1
, die erste Lösung sein kann sehr gut zurückkehren,libreoffice4.1
auch wenn/bin
vorher/usr/bin
in$PATH
). Inzsh
ist das$path
Array an den$PATH
Skalar gebunden (ein Merkmal von csh entlehnt)$path
kann geändert werden, ohne zu ändern$PATH
? Jedenfalls weiß ich sehr wenig darüber - nur, dass es sich um getrennte Dinge handelte. Nein - getestet - das Ändern hat sich$path
auch geändert$PATH
.$path
und$PATH
sind gebunden. Jede Änderung des einen spiegelt sich im anderen wider. Siehe die Beschreibung vontypeset -T
.$PATH
und miteinander verknüpft werden$path
. Das optionale dritte Argument ist ein einstelliges Trennzeichen, mit dem die Elemente des Arrays zum Skalar zusammengefügt werden. Wenn nicht vorhanden, wird ein Doppelpunkt verwendet.zsh
Sicher macht es ein paar nette Sachen, es ist nur so, dass es so viel davon macht, dass ich mich oft verliere. Deshalb habe ich gelernt, für die komplizierten Dingeksh93
viel besser zu mögen - es ist einfacher, wie ich denke. Aber das ist nur Präferenz.Hier ist eine einfachere Alternative:
Es wird davon ausgegangen, dass Bash und nur eine
libreoffice*
installiert ist.Es emuliert, was die Vervollständigung der Bash-Registerkarte tun würde, wenn Sie tippen
libreoffice
Tab.Wenn Sie absichtlich versucht haben,
libreoffice
ohne Versionsnummer auszuschließen , und die Existenz mehrerer Versionen behandeln möchten, versuchen Sie Folgendes:Die
case
Anweisung stimmt nur übereinlibreoffice?.?
, und wir durchlaufen die Ergebnisse und führen nur die erste aus.quelle
bash
- es wird ein vollständiges Ergebnis pro Zeile und keine$IFS
oder globale Zeichen in den Ergebnissen angenommen.compgen
das Muster zucase
erstellen.Sie können den Befehl "find" wie folgt verwenden
quelle
$PATH
, nicht den$PWD
POSIXly, wenn Sie nach einem
$PATH
ausführbaren Befehl suchen , sollten Sie Folgendes verwendencommand
:command -v
Wenn eithout einen Schalter genannt,
command
wird aufrufen command_name , aber Sie müssen noch eine voll aufgelösten Pfad eines vollständig lösen Shell - Glob .Um dies zu tun, sollten Sie den Glob gegen jedes durch
:
Doppelpunkte getrennte Element$PATH
in der angegebenen Reihenfolge prüfen :PATH
:
. Wenn auf diesen Dateinamen ein Präfix ungleich Null angewendet wird, wird zwischen dem Präfix und dem Dateinamen ein Schrägstrich eingefügt. Ein Präfix mit der Länge Null ist eine Legacy-Funktion , die das aktuelle Arbeitsverzeichnis angibt. Es wird als zwei benachbarte Doppelpunkte angezeigt::
, als anfänglicher Doppelpunkt vor dem Rest der Liste oder als nachfolgender Doppelpunkt nach dem Rest der Liste. Eine streng konforme Anwendung muss einen tatsächlichen Pfadnamen (z. B..
) verwenden , um das aktuelle Arbeitsverzeichnis in darzustellenPATH
. Die Liste wird von Anfang bis Ende durchsucht, wobei der Dateiname auf jedes Präfix angewendet wird, bis eine ausführbare Datei mit dem angegebenen Namen und den entsprechenden Ausführungsberechtigungen gefunden wird. Wenn der gesuchte Pfadname einen Schrägstrich enthält, wird die Suche durch die Pfadpräfixe nicht durchgeführt. Wenn der Pfadname mit einem Schrägstrich beginnt, wird der angegebene Pfad aufgelöst (siehe Pfadnamenauflösung ) . WennPATH
nicht gesetzt oder auf null gesetzt ist, ist die Pfadsuche implementierungsdefiniert .Sie können aufzuschlüsseln
$PATH
in ein Feld pro$PATH
On - Komponente$IFS
, aber Sie können dies nur tun , sicher , wenn Sie auchset -f
ilename Expansion als deaktiviert , wenn Sie das tun, falls eine Komponente in$PATH
einem enthält[?*
Zeichen, weil Dateinamen Generation (sprich: Globbing) tritt nach Feld -Splitting in der Erweiterungsreihenfolge der Shell.Auf diese Weise besteht die einzige wirklich robuste Möglichkeit, dies POSIXly zu tun, darin, zuerst die Feldliste zu erweitern,
$IFS
während die Dateinamengenerierung deaktiviert ist, und das Array-Ergebnis irgendwie zu speichern, dann die Dateinamengenerierung erneut zu aktivieren und die Feldaufteilung zu deaktivieren ( so dass Sie vielleicht speichern und Ihre glob Muster erweitern unquoted in einer variablen w / out jede Gefahr davon durch folgende Faktoren beeinträchtigt werden$IFS
zuerst) , und arbeiten auf den Ergebnissen jeder der Reihe nach .Zum Glück ist das nicht so schwierig:
Die letzten Teile dort werden nicht kommentiert, da sie hier besser erklärt werden.
for p do
- Die erstefor
Schleife weist iterativ$p
den Wert jedes Positionsparameters im Array der Shell zu"$@"
,set
der der Feldaufteilungserweiterung von$PATH
geteilt durch:
Doppelpunkte entspricht.for x in ${p:+"$p/"$g}
- Die innerefor
Schleife setzt iterativ$x
auf jede (falls vorhanden) der Erweiterungen von${p:+"$p/"$g}
- das heißt, sie iteriert überhaupt über nichts, es sei denn, sie$p
ist sowohl gesetzt als auch nicht null. Dies ist wichtig, weil::
oder ein Leading in:
Nullfelder aufgeteilt würde und auf diese Weise die beiden Schleifen genau der$PATH
Spezifikation (wie oben angegeben) entsprechen, indem Felder mit der Länge Null nicht anerkannt werden.Da die Art
$PATH
und Weise geteilt wird, während die Dateinamengenerierung deaktiviert ist und"$p/"$g
aufgelöst wird, während sie aktiviert ist, aber die Feldaufteilung deaktiviert wurde, kann keines der Elemente in$PATH
etwas anderes als ihre Felder als geteilt erweitert werden:
(unabhängig von den anderen Zeichen, die sie enthalten kann enthalten) und es kann auch kein Wert für die"$p/"$g
Erweiterung auf etwas anderes als sich selbst oder einen beliebigen Dateinamen verwendet werden (unabhängig davon, ob Zeichen in$p
oder andere als Mustervergleichszeichen$g
enthalten sind) .command
- undcommand
druckt zuletzt nur die Werte aus, für$x
die ausführbare Befehle standardisiert sind.Hier ist das Ganze in einer Shell-Funktion verpackt, mit der zusätzlichen Möglichkeit, mehrere
$g
Argumente zu verarbeiten:Damit erhalten Sie eine Liste der ausführbaren Dateien, die mit jedem Argument übereinstimmen, das Sie übergeben. Es wird eine pro Zeile gedruckt, die zuerst nach Argumentreihenfolge, dann nach
$PATH
Priorität und zuletzt nach Sortierreihenfolge Ihres Gebietsschemas sortiert ist. Es sollte nicht bei Zeilenumbrüchen oder Sonderzeichen in den Pfadnamen oder den Glob-Mustern fehlschlagen, die Sie ihm geben. Es kann geändert werden (siehe Bearbeitungsverlauf hier) , um das Shell-"$@"
Array mit einer ausführbaren Datei pro Positionsparameter genau zu füllen.Sie können es auch ändern, um das erste erfolgreiche Glob-Match auszuführen und die Suche nach diesem bestimmten Glob-Argument zu beenden und mit dem nächsten fortzufahren, wenn der Befehl erfolgreich beendet wird, wie ...
Dies ist ein tragbares POSIX-Mittel, um Ihre Glob-Übereinstimmung zu finden, aber einige Shells benötigen nicht einmal die zweite
for g...
Schleife. Inbash
funktioniert dies beispielsweise auch wie ...... die weiterhin alle ausführbaren Glob-Übereinstimmungen für alle Argumente und für jedes Element
$PATH
auflistet, unabhängig davon, ob eines dieser Elemente oder Glob-Muster Zeilenumbrüche oder Sonderzeichen usw. enthält.Sie können es verwenden wie:
... was für mich druckt ...
Wie bereits erläutert,
glob_path
interpretiert die Funktion die$PATH
Umgebungsvariable so, wie es in der Spezifikation angegeben ist, dass eine streng konforme Anwendung durch Ignorieren führender, nachfolgender oder aufeinanderfolgender:
Vorkommen im$PATH
Wert von 'sollte, interpretiert jedoch ein leeres oder nicht gesetztes$PATH
Mittel als Bedeutung.
.Wenn Sie diese Legacy-Funktion jedoch wollten, könnte die Funktion geschrieben werden:
... die alle Vorkommen von führenden, nachfolgenden oder zwei aufeinanderfolgenden
:
Doppelpunkten als Mittelwert interpretieren würden.
(und so.
oft suchen, wie eine$PATH
Komponente mit der Länge Null im$PATH
Wert von erscheint) , aber das ist nicht streng konform.quelle
$PATH
leere Komponenten enthalten sind. Beachten Sie auch, dass IFS Folgendes berücksichtigt: als Trennzeichen, nicht als Trennzeichen, da Sie $ PATH verarbeiten müssen (Werte von$PATH
like/bin:/usr/bin:
sind nicht ungewöhnlich)$PATH
Komponente enthalten sein kann:
? Wie auch immer; Ich habe das::
Ding repariert.IFS=:; $PATH
teilt sich/bin:/usr/bin:
in(/bin /usr/bin)
in POSIX-Shells auf, nicht(/bin /usr/bin '')
(Ausnahmezsh
in Sh-Emulation, Yash, PDKSH und älteren Versionen von MKSH)/bin:
bedeutet Suche in / bin und im aktuellen Verzeichnis. Für einen nicht gesetzten $ PATH, der von der Shell abhängt, überprüfen Sie meine Antwort auf das Warum nicht welche Frage verwenden . Eine korrektere Methode finden Sie unter @Gilles '.