Doppelte Einträge aus der PATH-Variablen entfernen

9

Ich ändere meine .bashrc häufig und beziehe sie dann. Wenn ich jedoch Dinge export PATH="~/bin:~/perl5/bin:$PATH"in meiner Datei habe, PATHwächst die Umgebungsvariable jedes Mal , wenn ich die Datei beschaffe.

Wenn zum Beispiel .bashrc zum ersten Mal bezogen wird, PATHbesteht die Variable aus ~/bin:~/perl5/bin:/usr/bin:/bin.

Das zweite Mal besteht es aus ~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

Das dritte Mal besteht es aus ~/bin:~/perl5/bin:~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

Gibt es eine einfache Möglichkeit, nur etwas hinzuzufügen, das noch nicht im PATH enthalten ist?

Christopher Bottoms
quelle

Antworten:

12

Verwenden Sie die in den meisten Distributionen verfügbare Funktion pathmunge () /etc/profile:

pathmunge () {
if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
   if [ "$2" = "after" ] ; then
      PATH=$PATH:$1
   else
      PATH=$1:$PATH
   fi
fi
}

Bearbeiten : Für zshBenutzer typeset -U <variable_name>werden Pfadeinträge dedupliziert.

Sam Halicke
quelle
Vielen Dank! Es ist nicht in /etc/profileDebian Lenny, also nehme ich es in meine auf .bashrc.
Christopher Bottoms
Verbrauch: pathmunge /some/pathfindet /some/patham Anfang $PATHund pathmunge /some/path afterfindet /some/patham Ende$PATH
Christopher Bottoms
Sauberere Methode zur Überprüfung, ob das Hew-Verzeichnis im aktuellen Pfad vorhanden ist, in modernen Bash-Shells : if ! [[ $PATH =~ (^|:)$1($|:) ]] ; then.
Christopher Cashell
Ich würde das nicht benutzen. Wenn meine naive Version PATH = / foo: $ PATH sagt, bedeutet das, dass ich möchte, dass / foo gewinnt. pathmunge /foo beforeschafft das nicht. Ein besserer Weg wäre, am Anfang / foo hinzuzufügen und anschließend Duplikate zu entfernen.
Don Hatch
3

Ich hatte dieses Problem, also habe ich eine Kombination von Techniken verwendet, die in der StackOverflow- Frage aufgeführt sind. Folgendes habe ich verwendet, um die tatsächlich festgelegte PATH-Variable zu deduplizieren, da ich das Basisskript nicht ändern wollte.

    tmppath=(${PATH// /@})
    array=(${tmppath//:/ })
    for i in "${array[@]//@/ }"
    do
        if ! [[ $PATH_NEW =~ "$i" ]]; then
            PATH_NEW="${PATH_NEW}$i:";
        fi
    done;
    PATH="${PATH_NEW%:}"
    export PATH
    unset PATH_NEW

Sie könnten dies immer etwas weiter optimieren, aber ich hatte zusätzlichen Code in meinem Original, um anzuzeigen, was passiert ist, um sicherzustellen, dass die Variablen korrekt eingestellt wurden. Die andere Sache zu beachten ist, dass ich Folgendes durchführe

  1. Ersetzen Sie ein Leerzeichen durch ein @ -Zeichen
  2. Teilen Sie das Array
  3. Schleife durch das Array
  4. Ersetzen Sie alle @ -Zeichen in der Elementzeichenfolge durch ein Leerzeichen

Dies soll sicherstellen, dass ich Verzeichnisse mit Leerzeichen in verarbeiten kann (Samba-Ausgangsverzeichnisse mit Active Directory-Benutzernamen können Leerzeichen enthalten!).

netniV
quelle
WARNUNG: Diese Implementierung hat einen großen Fehler !! Es wird entfernt, /binwenn es andere Einträge gibt, z. B. /usr/binweil der reguläre Ausdruck übereinstimmt, obwohl die beiden Einträge nicht identisch sind!
Sukima
Hier ist eine alternative Implementierung, die diesen Fehler behebt: github.com/sukima/dotfiles/blob/…
Sukima
1

Legen Sie Ihren Pfad explizit fest.

Cakemox
quelle
Vielen Dank. Ich habe versucht, den Pfad explizit festzulegen, aber ich habe eine .bashrc-Datei, die ich in mehreren Umgebungen verwende, sodass der genaue Standard PATHnicht immer der gleiche ist.
Christopher Bottoms
1

Nur eine Zeichenfolge:

for i in $(echo $PATH|tr ":" "\n"|sort|uniq);do PATH_NEW="${PATH_NEW}$i:";done;PATH="${PATH_NEW%:}"
bindbn
quelle
Vielen Dank. Eine Schwierigkeit, die mich dazu bringen würde, ist, dass der Inhalt von alphabetisch (oder ASCIIbetisch) neu angeordnet wird $PATH. Ich möchte bestimmte Verzeichnisse am Anfang von $PATH(like /home/username) einfügen, damit meine persönlichen Kopien von ausführbaren Dateien anstelle der integrierten Standardeinstellungen ausgeführt werden.
Christopher Bottoms
1

Ich kann mir zwei verschiedene Möglichkeiten vorstellen, wie Sie dies lösen können. Die erste besteht darin, Ihre .bashrc-Datei mit einer Zeile zu starten, in der Ihr Basis-PATH explizit festgelegt wird. Auf diese Weise wird die Basis jedes Mal, wenn Sie sie als Quelle verwenden, auf die Basis zurückgesetzt, bevor zusätzliche Verzeichnisse hinzugefügt werden.

Fügen Sie zum Beispiel hinzu:

# Reset the PATH to prevent duplication and to make sure that we include
# everything we want.
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Alternativ können Sie nach einem Element suchen, bevor Sie es dem Pfad hinzufügen. Dazu verwenden Sie Folgendes:

if ! [[ $PATH =~ '~/perl5/bin' ]]
then
    PATH="~/perl5/bin:$PATH"
fi

Letzteres wiederholt sich ein wenig, wenn Sie jedoch viele Einträge hinzufügen, sodass ich mich eher an Ersteres halte. Wenn Sie dies verwenden möchten und viele Einträge hinzufügen möchten, ist es ratsam, eine Bash-Funktion zu schreiben, um damit umzugehen.

Hinweis: Die zweite Option funktioniert möglicherweise nur wie in modernen Bash-Versionen beschrieben. Die Unterstützung für reguläre Ausdrücke ist keine Bourne-Shell-Funktion (/ bin / sh) und möglicherweise nicht in anderen Shells vorhanden. Außerdem ist die Verwendung von Anführungszeichen möglicherweise nicht erforderlich oder kann bei einigen neuesten Versionen von Bash sogar zu Problemen führen.

Christopher Cashell
quelle
Vielen Dank. Ich habe versucht, den Pfad explizit festzulegen, aber ich habe eine .bashrc-Datei, die ich in mehreren Umgebungen verwende, sodass der genaue Standard PATHnicht immer der gleiche ist.
Christopher Bottoms
Sie können dies weiterhin tun, indem Sie im Skript nachsehen, wie Ihr lokaler Hostname lautet, und dann Ihren absoluten Pfad entsprechend für diesen Server festlegen.
Christopher Cashell
Nur ein Tippfehler: die! fehlt in der if. Außerdem ist es (für mich) überraschend einfach, daraus eine Funktion zu machen, die durch Leerzeichen getrennte Argumente add_to_PATH ~/perl5/bin ~/.bin durchläuft. Sie können also einfach sagen: unix.stackexchange.com/a/4973/28760 (Ich denke, die dort verwendete caseAnweisung ist portabler , aber dein ifist mir klarer). BEARBEITEN Sie seltsamen Zufall, dass diese Frage auch perl5 hinzufügt!
13ren
@ 13ren: Danke für den Hinweis. Ich habe auch gerade festgestellt, dass ich einfache Anführungszeichen in der PATH-Set-Zeile anstelle von doppelten Anführungszeichen verwendet habe, wie ich es hätte tun sollen. Wie geschrieben, hätte es Ihren vorhandenen Pfad durch den Variablennamen ersetzt. Hoppla! Anscheinend war das ein schlechter Eintrag für mich für Tippfehler; beide sind jetzt behoben.
Christopher Cashell
0

Hier ist meine Lösung: PATH=$(echo -n $PATH | awk -v RS=: -v ORS=: '!x[$0]++' | sed "s/\(.*\).\{1\}/\1/")

Ein schöner, einfacher Einzeiler, der keine Spur hinterlässt:

AJ.
quelle