Hat jemand eine Bash-Funktion geschrieben, um $ PATH ein Verzeichnis hinzuzufügen, wenn es noch nicht vorhanden ist?
Normalerweise füge ich PATH hinzu, indem ich Folgendes verwende:
export PATH=/usr/local/mysql/bin:$PATH
Wenn ich meinen PATH in .bash_profile konstruiere, wird er nur gelesen, wenn es sich bei der Sitzung, in der ich mich befinde, um eine Anmeldesitzung handelt. Dies ist nicht immer der Fall. Wenn ich meinen PATH in .bashrc konstruiere, wird er mit jeder Subshell ausgeführt. Wenn ich also ein Terminal-Fenster starte und dann screen und dann ein Shell-Skript ausführe, erhalte ich:
$ echo $PATH
/usr/local/mysql/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:....
Ich werde versuchen, eine Bash-Funktion namens zu add_to_path()
erstellen, die das Verzeichnis nur hinzufügt, wenn es nicht vorhanden ist. Aber wenn jemand schon so etwas geschrieben (oder gefunden) hat, werde ich nicht die Zeit darauf verwenden.
Antworten:
Aus meiner .bashrc:
Beachten Sie, dass PATH bereits als exportiert markiert sein sollte, sodass ein erneutes Exportieren nicht erforderlich ist. Dies prüft, ob das Verzeichnis existiert & ein Verzeichnis ist, bevor es hinzugefügt wird, was Sie möglicherweise nicht interessiert.
Dadurch wird auch das neue Verzeichnis am Ende des Pfads hinzugefügt. Verwenden Sie
PATH="$1${PATH:+":$PATH"}"
anstelle der obigenPATH=
Zeile , um am Anfang zu setzen .quelle
":$PATH:"
statt nur"$PATH"
${PATH:+"$PATH:"}
${variable:+value}
bedeutet zu überprüfen, obvariable
definiert ist und einen nicht leeren Wert hat und ob es das Ergebnis der Auswertung gibtvalue
. Grundsätzlich gilt: Wenn PATH von Anfang an nicht leer ist, wird es auf gesetzt"$PATH:$1"
. Wenn es leer ist, setzt es es auf nur"$1"
(beachten Sie das Fehlen eines Doppelpunkts).In Anbetracht der Antwort von Gordon Davisson unterstützt dies mehrere Argumente
Sie können also pathappend path1 path2 path3 ...
Zum Voranstellen
Ähnlich wie bei pathappend können Sie dies tun
pathprepend path1 path2 path3 ...
quelle
pathprepend P1 P2 P3
und am Ende mitPATH=P1:P2:P3
. Um dieses Verhalten zu erhalten, wechseln Siefor ARG in "$@" do
zu "for ((i=$#; i>0; i--)); do ARG=${!i}
Hier ist etwas aus meiner Antwort auf diese Frage in Verbindung mit der Struktur der Funktion von Doug Harris. Es verwendet reguläre Bash-Ausdrücke:
quelle
$1
anstatt${1}
Fügen Sie dies in die Kommentare der ausgewählten Antwort ein, aber Kommentare scheinen die PRE-Formatierung nicht zu unterstützen. Fügen Sie die Antwort hier hinzu:
@ Gordon-Davisson Ich bin kein großer Fan von unnötigem Zitieren und Verketten. Angenommen, Sie verwenden eine Bash-Version> = 3, können Sie stattdessen Bashs eingebaute Regex-Befehle verwenden und Folgendes ausführen:
Dies behandelt Fälle korrekt, in denen sich Leerzeichen im Verzeichnis oder im PATH befinden. Es ist fraglich, ob die in bash eingebaute Regex-Engine langsam genug ist, um weniger effizient zu sein als die String-Verkettung und -Interpolation, die Ihre Version durchführt, aber irgendwie fühlt sie sich für mich nur ästhetisch sauberer an.
quelle
formatting using the backtick
nur unterstützt, aber Sie erhalten keine anständige Absatzkontrolle.unnecessary quoting
bedeutet, dass Sie im Voraus wissen, dass dies$PATH
nicht null ist."$PATH"
macht es OK, ob PATH null ist oder nicht. In ähnlicher Weise$1
enthält if Zeichen, die den Befehlsparser verwirren könnten. Das Setzen des regulären Ausdrucks in Anführungszeichen"(^|:)$1(:|$)"
verhindert dies.[[
und unterschiedlich sind]]
. Ich ziehe alles zu zitieren , die möglicherweise brauchen kann angegeben werden, es sei denn , die bewirkt , dass es zum Scheitern verurteilt, aber ich glaube , er ist richtig, und dass Zitate wirklich nicht benötigt um$PATH
. Auf der anderen Seite scheint es mir, dass Sie Recht haben$1
.Wenn $ HOME / bin genau einmal am Anfang Ihres $ PATH und an keiner anderen Stelle erscheinen soll, akzeptieren Sie keine Ersatzzeichen.
quelle
PATH=${PATH/"
... anstatt ... etwas tun musstePATH=${PATH//"
, damit es funktioniert.$1
der einzige Eintrag ist (keine Doppelpunkte). Der Eintrag wird verdoppelt.Hier ist eine alternative Lösung, die den zusätzlichen Vorteil hat, redundante Einträge zu entfernen:
Das einzige Argument für diese Funktion wird dem Pfad vorangestellt, und die erste Instanz derselben Zeichenfolge wird aus dem vorhandenen Pfad entfernt. Mit anderen Worten, wenn das Verzeichnis bereits im Pfad vorhanden ist, wird es nach vorne verschoben, anstatt als Duplikat hinzugefügt zu werden.
Bei dieser Funktion wird dem Pfad ein Doppelpunkt vorangestellt, um sicherzustellen, dass alle Einträge vorne einen Doppelpunkt haben. Anschließend wird der neue Eintrag dem vorhandenen Pfad vorangestellt, wobei dieser Eintrag entfernt wird. Der letzte Teil wird in der Bash-
${var//pattern/sub}
Notation ausgeführt. Weitere Informationen finden Sie im Bash-Handbuch .quelle
/home/robert
in IhremPATH
und Ihnen habenpathadd /home/rob
.Hier ist meins (ich glaube, es wurde vor Jahren von Oscar geschrieben, dem Systemadministrator meines alten Labors, der ihm alle Ehre macht). Es hat den zusätzlichen Vorteil, dass Sie das neue Verzeichnis wie gewünscht voranstellen oder anhängen können:
Verwendungszweck:
quelle
Zum Voranstellen mag ich die Lösung von @ Russell, aber es gibt einen kleinen Fehler: Wenn Sie versuchen, etwas wie "/ bin" vor einen Pfad von "/ sbin: / usr / bin: / var / usr / bin: / usr / local" zu stellen / bin: / usr / sbin "es ersetzt" / bin: "3 mal (wenn es überhaupt nicht wirklich passt). Wenn ich einen Fix dafür mit der anhängenden Lösung von @ gordon-davisson kombiniere, erhalte ich Folgendes:
quelle
Ein einfacher Alias wie dieser unten sollte den Trick machen:
Sie müssen lediglich den Pfad des Zeichens: aufteilen und jede Komponente mit dem Argument vergleichen, das Sie übergeben. Grep prüft, ob eine vollständige Zeilenübereinstimmung vorliegt, und gibt die Anzahl aus.
Beispielnutzung:
Ersetzen Sie den Befehl echo durch addToPath oder einen ähnlichen Alias / eine ähnliche Funktion.
quelle
Siehe So verhindern Sie das Duplizieren von Pfadvariablen in csh auf StackOverflow für eine Reihe von Antworten auf diese Frage.
quelle
Folgendes habe ich mir ausgedacht:
Jetzt in .bashrc habe ich:
Aktualisierte Version mit folgendem Kommentar darüber, wie mein Original Verzeichnisse mit Leerzeichen nicht behandelt (dank dieser Frage, die mich auf die Verwendung hinweist
IFS
):quelle
PATH
Leerzeichen enthält,*
,?
, oder[
...]
.Documents and Settings
undProgram Files
), aber Unix hat Pfadnamen mit Leerzeichen unterstützt , seitdem Windoze existiert hat.Ich bin ein bisschen überrascht, dass dies noch niemand erwähnt hat, aber Sie können
readlink -f
relative Pfade in absolute Pfade konvertieren und sie als solche zum PATH hinzufügen.Um beispielsweise die Antwort von Guillaume Perrault-Archambault zu verbessern,
wird
1. Grundlagen - Was nützt das?
Der
readlink -f
Befehl konvertiert (unter anderem) einen relativen Pfad in einen absoluten Pfad. Dies ermöglicht es Ihnen, etwas zu tun2. Warum testen wir zweimal, ob wir im Pfad sind?
Betrachten Sie das obige Beispiel. Wenn der Benutzer aus dem Verzeichnis ein zweites Mal sagt , wird . Natürlich wird in nicht vorhanden sein . Dann aber wird eingestellt (die absoluten Pfad äquivalent ), das ist in bereits . Wir müssen also vermeiden , ein zweites Mal etwas hinzuzufügen .
pathappend .
/path/to/my/bin/dir
ARG
.
.
PATH
ARGA
/path/to/my/bin/dir
.
PATH
/path/to/my/bin/dir
PATH
Vielleicht noch wichtiger ist, dass der Hauptzweck von
readlink
, wie der Name schon sagt, darin besteht, einen symbolischen Link zu betrachten und den darin enthaltenen Pfadnamen auszulesen (dh auf diesen zu zeigen). Zum Beispiel:Nun, wenn Sie sagen
pathappend /usr/lib/perl/5.14
, und Sie haben bereits/usr/lib/perl/5.14
in Ihrem Pfad, nun, das ist in Ordnung; wir können es einfach so lassen, wie es ist. Aber, wenn/usr/lib/perl/5.14
nicht bereits in Ihrer PATH ist, rufen wirreadlink
und bekommenARGA
=/usr/lib/perl/5.14.2
und dann fügen wir , dass zuPATH
. Aber warte eine Minute - wenn du es schon gesagt hastpathappend /usr/lib/perl/5.14
, dann hast du es bereits/usr/lib/perl/5.14.2
in deinem PFAD und wir müssen es erneut überprüfen, um zu vermeiden, dass esPATH
ein zweites Mal hinzugefügt wird.3. Mit was ist das Geschäft
if ARGA=$(readlink -f "$ARG")
?Falls es unklar ist, prüft diese Zeile, ob das
readlink
erfolgreich ist. Dies ist nur eine gute, defensive Programmierpraxis. Wenn wir die Ausgabe von Befehl m als Teil von Befehl n verwenden (wobei m < n ist ), ist es ratsam, zu überprüfen, ob Befehl m fehlgeschlagen ist, und dies in irgendeiner Weise zu handhaben. Ich glaube nicht, dass dies wahrscheinlichreadlink
scheitern wird - aber wie in Wie man den absoluten Pfad einer beliebigen Datei von OS X und anderswo abruft ,readlink
handelt es sich um eine GNU-Erfindung. Es ist in POSIX nicht spezifiziert, daher ist seine Verfügbarkeit unter Mac OS, Solaris und anderen Nicht-Linux-Unixen fraglich. (Tatsächlich habe ich gerade einen Kommentar gelesen , der besagt: „readlink -f
funktioniert anscheinend nicht unter Mac OS X 10.11.6,realpath
funktioniert aber sofort . “Wenn Sie sich also auf einem System befinden, das nicht über ein System verfügtreadlink
oder auf demreadlink -f
es nicht funktioniert, können Sie dies möglicherweise ändern Skript zu verwendenrealpath
.) Durch die Installation eines Sicherheitsnetzes machen wir unseren Code etwas portabler.Wenn Sie sich auf einem System ohne
readlink
(oderrealpath
) befinden, werden Sie das natürlich nicht wollen .pathappend .
Der zweite
-d
Test ([ -d "$ARGA" ]
) ist wirklich wahrscheinlich unnötig. Ich kann mir kein Szenario vorstellen, in dem$ARG
ein Verzeichnis vorhanden undreadlink
erfolgreich ist, das jedoch$ARGA
kein Verzeichnis ist. Ich habe nur die ersteif
Anweisung kopiert und eingefügt , um die dritte Anweisung zu erstellen, und den-d
Test dort aus Faulheit verlassen.4. Andere Kommentare?
Ja. Wie viele der anderen Antworten hier testet dieses Argument, ob es sich um ein Verzeichnis handelt, verarbeitet es, wenn dies der Fall ist, und ignoriert es, wenn dies nicht der Fall ist. Dies kann (oder auch nicht) angemessen sein, wenn Sie
pathappend
nur.
Dateien (like.bash_profile
und.bashrc
) und andere Skripte verwenden. Aber wie diese Antwort (oben) gezeigt hat, ist es durchaus machbar, sie interaktiv zu nutzen. Sie werden sehr verwirrt sein, wenn Sie dies tunWie ich oben sagte, ist es eine gute Praxis, mit Fehlern umzugehen. Hier ist ein Beispiel:
quelle
readlink -f "$ARG"
. (2) Ich weiß nicht, warum dies passieren würde (insbesondere nachdem der-d "$ARG"
Test erfolgreich war), aber Sie möchten möglicherweise testen, ob der Testreadlink
fehlgeschlagen ist. (3) Sie scheinen diereadlink
Hauptfunktion von übersehen zu haben - einen symbolischen Linknamen einem "echten Dateinamen" zuzuordnen. Wenn (zum Beispiel)/bin
eine symbolische Verknüpfung zu ist/bin64
, kann ein wiederholter Aufruf vonpathappend /bin
PATH zu einer Meldung führen…:/bin64:/bin64:/bin64:/bin64:…
. Sie sollten wahrscheinlich (auch) überprüfen, ob der neue Wert von$ARG
bereits in istPATH
..
ich in meinem Beispiel für einen kanonischen Dateinamen halte . Richtig?readlink
impliziert der Name eindeutig seinen Zweck - er betrachtet eine symbolische Verknüpfung und liest den darin enthaltenen Pfadnamen aus (dh zeigt auf). (2) Nun, ich sagte , ich wüsste nicht, warumreadlink
es scheitern würde. Ich machte den allgemeinen Punkt, dass, wenn ein Skript oder eine Funktion mehrere Befehle enthält und der Befehl n garantiert katastrophal fehlschlägt (oder überhaupt keinen Sinn ergibt), wenn der Befehl m fehlschlägt (wobei m < n ist ), es ratsam ist, dies zu tun Überprüfen Sie, ob der Befehl m fehlgeschlagen ist, und behandeln Sie das auf irgendeine Weise - zumindest,… (Fortsetzung)readlink
könnte dies fehlschlagen, wenn (a) das Verzeichnis zwischen Ihren Aufrufen vontest
und umbenannt oder (durch einen anderen Prozess) gelöschtreadlink
wurde oder (b)/usr/bin/readlink
gelöscht (oder beschädigt) wurde. (3) Sie scheinen meinen Standpunkt zu verfehlen. Ich ermutige Sie nicht, andere Antworten zu duplizieren. Ich sage, dass Ihre Antwort unvollständig und daher falsch ist , wenn Sie prüfen, ob das OriginalARG
(von der Befehlszeile) bereits vorhanden istPATH
, aber die Prüfung für das Neue (die Ausgabe von ) nicht wiederholen . … (Fortsetzung)ARG
readlink
quelle
Dieser Weg funktioniert gut:
quelle
Meine Versionen sind weniger vorsichtig mit leeren Pfaden und bestehen darauf, dass Pfade und Verzeichnisse gültig sind als einige hier veröffentlichte, aber ich finde eine umfangreiche Sammlung von prepend / append / clean / unique-ify / etc. Shell-Funktionen sind nützlich für die Manipulation von Pfaden. Die ganze Menge in ihrem aktuellen Zustand finden Sie hier: http://pastebin.com/xS9sgQsX (Feedback und Verbesserungen sehr willkommen!)
quelle
Sie können einen Perl-Einzeiler verwenden:
Hier ist es in Bash:
quelle
Ich habe die Antwort von Gordon Davisson leicht geändert , um das aktuelle Verzeichnis zu verwenden, falls keines angegeben ist. Sie können dies also einfach
padd
aus dem Verzeichnis heraus tun, das Sie Ihrem PATH hinzufügen möchten.quelle
Sie können überprüfen, ob eine benutzerdefinierte Variable festgelegt wurde, andernfalls festlegen und dann die neuen Einträge hinzufügen:
Natürlich können diese Einträge immer noch dupliziert werden, wenn sie von einem anderen Skript hinzugefügt werden, z
/etc/profile
.quelle
Mit diesem Skript können Sie am Ende hinzufügen
$PATH
:Oder am Anfang hinzufügen
$PATH
:quelle
Hier ist ein POSIX-konformer Weg.
Es ist aus cribbed Guillaume Perrault-Archambault ‚s Antwort auf diese Frage und mike511 ‘ s Antwort hier .
UPDATE 23.11.2017: Fehler pro @Scott behoben
quelle
/etc/profile
. Das Verzeichnis, das Sie PATH hinzufügen möchten, ist möglicherweise bereits mehr als einmal vorhanden , und Ihr Code ist daran gehindert. … (Fortsetzung)/a/ck
zu/b:/a/ck:/tr:/a/ck
, Sie erhalten/a/ck:/b:/a/ck:/tr:/tr:/a/ck
.