Wie kann ich $ PATH sauber hinzufügen?

31

Ich möchte eine Möglichkeit haben, Dinge systemweit oder für einen einzelnen Benutzer zu $ ​​PATH hinzuzufügen, ohne möglicherweise den gleichen Pfad mehrmals hinzuzufügen.

Ein Grund, dies zu tun, besteht darin, dass Ergänzungen vorgenommen werden können, für .bashrcdie keine Anmeldung erforderlich ist, und dies ist auch auf Systemen, die (z. B.) verwenden lightdmund die niemals anrufen, nützlicher .profile.

Ich kenne Fragen zum Entfernen von Duplikaten aus $ PATH, möchte jedoch keine Duplikate entfernen . Ich möchte eine Möglichkeit, Pfade nur hinzuzufügen, wenn sie nicht bereits vorhanden sind.

Goldlöckchen
quelle
Möglicherweise doppelte
Kopien
goldi, ich weiß nicht warum aber ich hatte deinen ersten kommentar schon mit dem leeren gesehen. Aber ja, Namenspräfixe funktionieren auch, keine Sorge! Das Schließen in die andere Richtung ist ebenfalls in Ordnung.
Ciro Santilli
Okay, solange du meine Nachricht hast. Manchmal verursacht eine solche Umkehrung ein bisschen Chaos. Ich denke, wir werden sehen, was passiert.
Goldlöckchen

Antworten:

35

Angenommen, der neue Pfad, den wir hinzufügen möchten, lautet:

new=/opt/bin

Dann können wir mit einer beliebigen POSIX-Shell testen, ob newsich bereits ein Pfad befindet, und ihn hinzufügen, wenn dies nicht der Fall ist:

case ":${PATH:=$new}:" in
    *:"$new":*)  ;;
    *) PATH="$new:$PATH"  ;;
esac

Beachten Sie die Verwendung von Doppelpunkten. Ohne die Doppelpunkte könnten wir denken, dass sie new=/binsich beispielsweise bereits auf dem Weg befanden, weil sie mit dem Muster übereinstimmten /usr/bin. Während PATHs normalerweise viele Elemente enthalten, werden auch die Sonderfälle von Null- und Eins-Elementen im PATH behandelt. Der Fall, dass der PATH anfänglich keine Elemente enthält (leer ist), wird durch die Verwendung behandelt, ${PATH:=$new}die zuweist PATH, $newwenn er leer ist. Das Festlegen von Standardwerten für Parameter auf diese Weise ist eine Funktion aller POSIX-Shells: siehe Abschnitt 2.6.2 der POSIX-Dokumente .)

Eine aufrufbare Funktion

Der Einfachheit halber kann der obige Code in eine Funktion eingefügt werden. Diese Funktion kann in der Befehlszeile definiert oder dauerhaft im Initialisierungsskript Ihrer Shell abgelegt werden (für Bash-Benutzer wäre dies ~/.bashrc):

pupdate() { case ":${PATH:=$1}:" in *:"$1":*) ;; *) PATH="$1:$PATH" ;; esac; }

So verwenden Sie diese Pfadaktualisierungsfunktion, um dem aktuellen PFAD ein Verzeichnis hinzuzufügen:

pupdate /new/path
John1024
quelle
@ Hammar OK. Ich habe einen Fall dafür hinzugefügt.
John1024
1
Sie können 2 Fallunterscheidungen speichern - vgl. unix.stackexchange.com/a/40973/1131 .
Maxschlepzig
3
Wenn PATHleer, wird ein leerer Eintrag (dh das aktuelle Verzeichnis) zum. Hinzugefügt PATH. Ich denke, Sie brauchen einen anderen Fall.
CB Bailey
2
@ Charles Bailey Nicht ein anderer case. Tu es einfach case "${PATH:=$new}". Siehe meine eigene Antwort für ähnliche Fallbacks.
mikeserv
1
@ mc0e Ich habe ein Beispiel hinzugefügt, wie eine Shell-Funktion verwendet wird, um das "Zeilenrauschen" zu verbergen.
John1024
9

Erstellen Sie eine Datei in /etc/profile.daufgerufen, zB mypath.sh(oder was auch immer Sie wollen). Wenn Sie Lightdm verwenden, stellen Sie sicher, dass es funktionsfähig ist, oder verwenden Sie /etc/bashrceine Datei, die von derselben stammt. Dazu kommen folgende Funktionen:

checkPath () {
        case ":$PATH:" in
                *":$1:"*) return 1
                        ;;
        esac
        return 0;
}

# Prepend to $PATH
prependToPath () {
        for a; do
                checkPath $a
                if [ $? -eq 0 ]; then
                        PATH=$a:$PATH
                fi
        done
        export PATH
}

# Append to $PATH
appendToPath () {
        for a; do
                checkPath $a
                if [ $? -eq 0 ]; then
                        PATH=$PATH:$a
                fi
        done
        export PATH
}

Dinge am Anfang von (vor) $ PATH haben Vorrang vor dem, was folgt, und umgekehrt werden Dinge am Ende (angefügt) durch das, was vorher kommt, abgelöst. Dies bedeutet, wenn Ihr $ PATH ist /usr/local/bin:/usr/binund gotchain beiden Verzeichnissen eine ausführbare Datei vorhanden ist /usr/local/bin, wird standardmäßig diejenige in verwendet.

Sie können jetzt - in derselben Datei, in einer anderen Shell-Konfigurationsdatei oder über die Befehlszeile - Folgendes verwenden:

appendToPath /some/path /another/path
prependToPath /some/path /yet/another/path

Wenn dies in a steht .bashrc, wird verhindert, dass der Wert mehrmals angezeigt wird, wenn Sie eine neue Shell starten. Es gibt eine Einschränkung: Wenn Sie etwas anhängen möchten, das vorangestellt wurde (dh einen Pfad innerhalb von $ PATH verschieben) oder umgekehrt, müssen Sie dies selbst tun.

Goldlöckchen
quelle
das teilen $PATHmit IFS=:ist letztendlich flexibler als case.
mikeserv
@mikeserv Kein Zweifel. Dies ist eine Art Hack-Verwendung für caseIMO. Ich kann mir vorstellen, dass awkauch hier eine gute Verwendung gefunden werden könnte.
Goldlöckchen
Das ist ein guter Punkt. Und, wie ich finde,gawk direkt zuordnen können $PATH.
mikeserv
5

Du kannst es so machen:

echo $PATH | grep /my/bin >/dev/null || PATH=$PATH:/my/bin

Hinweis: Wenn Sie PATH aus anderen Variablen erstellen, stellen Sie sicher, dass diese nicht leer sind. Viele Shells interpretieren "" wie ". .

Gegenmodus
quelle
+1 Laut der Manpage -qist POSIX für grep erforderlich, aber ich weiß nicht, ob das bedeutet, dass es noch einige (nicht POSIX) greps gibt, die es nicht haben.
Goldlöckchen
1
Beachten Sie, dass das Grep-Muster zu breit ist. Erwägen Sie die Verwendung von egrep -q "(^ |:) / my / bin (: | \ $)" anstelle von grep / my / bin> / dev / null. Mit dieser Änderung ist Ihre Lösung korrekt und ich denke, dies ist eine besser lesbare Lösung als die derzeit bevorzugte Antwort von @ john1024. Beachten Sie, dass ich doppelte Anführungszeichen verwendet habe, sodass Sie die Variablensubstitution anstelle von/my/bin
mc0e
5

Der wichtige Teil des Codes besteht darin, zu überprüfen, ob PATHein bestimmter Pfad enthalten ist:

printf '%s' ":${PATH}:" | grep -Fq ":${my_path}:"

Stellen Sie also sicher, dass jeder Pfad PATHauf beiden Seiten durch das PATHTrennzeichen ( :) begrenzt ist, und überprüfen Sie ( -q), ob die Literalzeichenfolge ( -F) aus einem PATHTrennzeichen, Ihrem Pfad und einem anderen bestehtPATH Trennzeichen besteht, dort vorhanden ist. Wenn dies nicht der Fall ist, können Sie den Pfad problemlos hinzufügen:

if ! printf '%s' ":${PATH-}:" | grep -Fq ":${my_path-}:"
then
    PATH="${PATH-}:${my_path-}"
fi

Dies sollte POSIX-kompatibel sein und mit jedem Pfad funktionieren, der kein Zeilenumbruchzeichen enthält. Es ist komplexer, wenn Sie möchten, dass es mit Pfaden funktioniert, die Newline enthalten, während es POSIX-kompatibel ist. Wenn Sie jedoch einen haben, grepder dies unterstützt -z, können Sie diesen verwenden.

l0b0
quelle
4

Ich habe diese kleine Funktion ~/.profileseit Jahren in verschiedenen Akten mit mir herumgetragen. Ich glaube, es wurde vom Sysadmin in einem Labor geschrieben, in dem ich gearbeitet habe, aber ich bin mir nicht sicher. Wie auch immer, es ist Goldilocks Ansatz ähnlich, aber ein wenig anders:

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

So fügen Sie ein neues Verzeichnis am Anfang des folgenden Befehls hinzu PATH:

pathmunge /new/path

und bis zum ende:

pathmunge /new/path after
terdon
quelle
Das funktioniert bei mir! Aber ich habe auf Logik umgestellt, um es standardmäßig nachher zu setzen und mit "vorher" zu überschreiben. :)
Kevin Pauli
pathmunge ist ein teil von linux centos distribution / etc / profile, es hat einen parameter davor und danach. Ich sehe es nicht in meinem letzten Ubuntu 16.
Kemin Zhou
Scheint auf macOS 10.12 nach /bin/grep->grep
Ben Creasy
4

AKTUALISIEREN:

Mir ist aufgefallen, dass Ihre eigene Antwort eine separate Funktion zum Anhängen oder Voranstellen an die hat $PATH. Mir hat die Idee gefallen. Also habe ich ein kleines Argument hinzugefügt. Ich habe es auch richtig mit einem _Namensraum versehen:

_path_assign() { oFS=$IFS ; IFS=: ; add=$* ; unset P A ; A=
    set -- ${PATH:=$1} ; for p in $add ; do {
        [ -z "${p%-[AP]}" ] && { unset P A
                eval ${p#-}= ; continue ; }
        for d ; do [ -z "${d%"$p"}" ] && break
        done ; } || set -- ${P+$p} $* ${A+$p}
        done ; export PATH="$*" ; IFS=$oFS
}

% PATH=/usr/bin:/usr/yes/bin
% _path_assign \
    /usr/bin \
    /usr/yes/bin \
    /usr/bin/nope \
    -P \
    /usr/nope/bin \
    /usr/bin \
    -A \
    /nope/usr/bin \
    /usr/nope/bin

% echo $PATH

AUSGABE:

/usr/nope/bin:/usr/bin:/usr/yes/bin:/usr/bin/nope:/nope/usr/bin

Standardmäßig wird es -Aangehalten $PATH, aber Sie können dieses Verhalten -Pändern, indem Sie an einer -Pbeliebigen Stelle in Ihrer Argumentliste ein neues Argument hinzufügen . Sie können es wieder in den -AStandby-Modus versetzen, indem Sie es -Aerneut geben.

SICHERES EVAL

In den meisten Fällen empfehle ich, dass Menschen jede Verwendung von vermeiden eval. Aber ich denke, dies ist ein gutes Beispiel für seine Verwendung . In diesem Fall ist oder die einzige Anweisung, eval die jemals angezeigt werden kann . Die Werte seiner Argumente werden streng geprüft, bevor sie aufgerufen werden. Dafür ist .P=A=eval

assign() { oFS=$IFS ; IFS=: ; add=$* 
    set -- ${PATH:=$1} ; for p in $add ; do { 
        for d ; do [ -z "${d%"$p"}" ] && break 
        done ; } || set -- $* $p ; done
    PATH="$*" ; IFS=$oFS
}

Dies akzeptiert so viele Argumente, wie Sie angeben, und fügt sie jeweils $PATHnur einmal und nur dann hinzu, wenn sie nicht bereits vorhanden sind $PATH. Es wird nur ein vollständig portierbares POSIX-Shell-Skript verwendet, es basiert nur auf integrierten Shell-Funktionen und ist sehr schnell.

% PATH=/usr/bin:/usr/yes/bin
% assign \
    /usr/bin \
    /usr/yes/bin \
    /usr/nope/bin \
    /usr/bin \
    /nope/usr/bin \
    /usr/nope/bin

% echo "$PATH"
> /usr/bin:/usr/yes/bin:/usr/nope/bin:/nope/usr/bin
mikeserv
quelle
@Tafka'goldilocks 'siehe das update hier - du hast mich inspiriert.
mikeserv
+1 Aus Neugier (vielleicht wäre dies eine gute Frage und Antwort), woher kommt die Idee, dass die _vorangestellten Shell-Funktionen dafür sorgen, dass sie einen "richtigen Namensraum" haben? In anderen Sprachen gibt es normalerweise eine interne globale Funktion an (dh eine, die global sein muss, aber nicht für die externe Verwendung als Teil einer API vorgesehen ist). Meine Namen sind mit Sicherheit keine gute Wahl, aber es scheint mir _, dass Kollisionsprobleme durch die Verwendung von überhaupt nicht gelöst werden - es wäre besser, einen tatsächlichen Namespace zu verwenden, z. mikeserv_path_assign().
Goldlöckchen
@ TAFKA "Goldlöckchen" - es wäre besser, noch spezifischer damit umzugehen, aber je länger der Name wird, desto weniger bequem ist seine Verwendung. Wenn Ihnen jedoch geeignete ausführbare Binärdateien vorangestellt sind _, müssen Sie die Paketmanager wechseln. In jedem Fall handelt es sich im Wesentlichen nur um eine "globale, interne Funktion" - sie ist global für jede Shell, die von der Shell aufgerufen wird, in der sie deklariert wurde, und es handelt sich nur um ein interpretiertes Sprachskript, das im Speicher des Interpreters hängt . unix.stackexchange.com/questions/120528/…
mikeserv
Können Sie nicht unset a(oder gleichwertig) am Ende des Profils?
Sourcejedi
0

Erblicken! Die industrietaugliche 12er-Linie ... technisch bash- und zsh-portable Shell - Funktion , dass Ihre hingebungsvoll liebt ~/.bashrcoder ~/.zshrcStartskript der Wahl:

# void +path.append(str dirname, ...)
#
# Append each passed existing directory to the current user's ${PATH} in a
# safe manner silently ignoring:
#
# * Relative directories (i.e., *NOT* prefixed by the directory separator).
# * Duplicate directories (i.e., already listed in the current ${PATH}).
# * Nonextant directories.
+path.append() {
    # For each passed dirname...
    local dirname
    for   dirname; do
        # Strip the trailing directory separator if any from this dirname,
        # reducing this dirname to the canonical form expected by the
        # test for uniqueness performed below.
        dirname="${dirname%/}"

        # If this dirname is either relative, duplicate, or nonextant, then
        # silently ignore this dirname and continue to the next. Note that the
        # extancy test is the least performant test and hence deferred.
        [[ "${dirname:0:1}" == '/' &&
           ":${PATH}:" != *":${dirname}:"* &&
           -d "${dirname}" ]] || continue

        # Else, this is an existing absolute unique dirname. In this case,
        # append this dirname to the current ${PATH}.
        PATH="${PATH}:${dirname}"
    done

    # Strip an erroneously leading delimiter from the current ${PATH} if any,
    # a common edge case when the initial ${PATH} is the empty string.
    PATH="${PATH#:}"

    # Export the current ${PATH} to subprocesses. Although system-wide scripts
    # already export the ${PATH} by default on most systems, "Bother free is
    # the way to be."
    export PATH
}

Bereite dich auf den sofortigen Ruhm vor. Dann, anstatt dies zu tun und auf das Beste zu hoffen:

export PATH=$PATH:~/opt/bin:~/the/black/goat/of/the/woods/with/a/thousand/young

Tun Sie dies stattdessen und Sie werden garantiert das Beste bekommen, egal ob Sie das wirklich wollten oder nicht:

+path.append ~/opt/bin ~/the/black/goat/of/the/woods/with/a/thousand/young

Sehr gut, definieren Sie "am besten".

Das sichere Anhängen und Voranstellen an die aktuelle Situation ${PATH}ist keine alltägliche Angelegenheit. Obwohl es praktisch und vernünftig erscheint, export PATH=$PATH:~/opt/binladen Einzeiler der Form teuflische Komplikationen ein mit:

  • Versehentlich relative Dirnames (zB export PATH=$PATH:opt/bin). Während bashund zshleise akzeptieren und ignoriert meist relativ dirnames in den meisten Fällen als Präfix relativ dirnames von entweder hoder t(und möglicherweise anderen ruchlosen Zeichen) Ursache sowohl für sich schändlich ala Masaki Kobayashi verstümmeln Samen 1962 Meisterwerk Harakiri :

    # Don't try this at home. You will feel great pain.
    $ PATH='/usr/local/bin:/usr/bin:/bin' && export PATH=$PATH:harakiri && echo $PATH
    /usr/local/bin:/usr/bin:arakiri
    $ PATH='/usr/local/bin:/usr/bin:/bin' && export PATH=$PATH:tanuki/yokai && echo $PATH
    binanuki/yokai   # Congratulations. Your system is now face-up in the gutter.
  • Versehentlich doppelte Dirnames. Während doppelte ${PATH}Dirnames weitgehend harmlos sind, sind sie auch unerwünscht, umständlich, leicht ineffizient, beeinträchtigen die Debug-Fähigkeit und fördern den Verschleiß des Laufwerks - wie diese Antwort. Während SSDs im NAND-Stil ( natürlich ) unempfindlich gegen Leseverschleiß sind, sind dies HDDs nicht. Unnötiger Zugriff auf das Dateisystem bei jedem versuchten Befehl führt zu unnötigem Verschleiß des Lesekopfs im selben Tempo. Duplikate sind besonders unsauber, wenn Sie verschachtelte Shells in verschachtelten Unterprozessen aufrufen. An diesem Punkt sollten Sie scheinbar harmlose Einzeiler wie " Lassen Sie dies nicht zu, dass Ihre kostbaren Kinder dies tun". )export PATH=$PATH:~/wat explodieren schnell in den siebten Kreis von${PATH} der Hölle wie PATH=/usr/local/bin:/usr/bin:/bin:/home/leycec/wat:/home/leycec/wat:/home/leycec/wat:/home/leycec/wat. Nur Beelzebubba kann Ihnen helfen, wenn Sie zusätzliche Verzeichnisnamen anhängen. (

  • Versehentlich fehlende Dirnames. Auch wenn fehlende ${PATH}Dirnames weitgehend harmlos sind, sind sie in der Regel unerwünscht, umständlich, wenig effizient, beeinträchtigen die Debug-Fähigkeit und fördern den Verschleiß des Laufwerks.

Ergo, freundliche Automatisierung wie die oben definierte Shell-Funktion. Wir müssen uns vor uns selbst retten.

Aber ... Warum "+ path.append ()"? Warum nicht einfach append_path ()?

Für Disambiguierung (zB mit externen Kommandos in den aktuellen ${PATH}oder systemweite Shell - Funktionen definierten an anderer Stelle), benutzerdefinierte Shell - Funktionen sind ideal als Präfix oder Suffix mit einzigartigem Teil unterstützt durch bashund zshansonsten aber für Standardbefehl Basisnamen verboten - wie, sagen sie, +.

Hallo. Es klappt. Verurteile mich nicht.

Aber ... Warum "+ path.append ()"? Warum nicht "+ path.prepend ()"?

Weil das Anhängen an den Strom ${PATH}sicherer ist als das Voranhängen an den Strom ${PATH}, wenn alle Dinge gleich sind, was sie niemals sind. Das Überschreiben systemweiter Befehle mit benutzerspezifischen Befehlen kann bestenfalls unhygienisch sein und schlimmstenfalls verrückt machen. Unter Linux beispielsweise erwarten nachgelagerte Anwendungen üblicherweise die Befehlsvarianten GNU coreutils und nicht benutzerdefinierte, nicht standardmäßige Derivate oder Alternativen.

Allerdings gibt es dafür durchaus gültige Use Cases. Das Definieren der äquivalenten +path.prepend()Funktion ist trivial. Sans Prolix-Nebel, für seine und ihre gemeinsame Vernunft:

+path.prepend() {
    local dirname
    for dirname in "${@}"; do
        dirname="${dirname%/}"
        [[ "${dirname:0:1}" == '/' &&
           ":${PATH}:" != *":${dirname}:"* &&
           -d "${dirname}" ]] || continue
        PATH="${dirname}:${PATH}"
    done
    PATH="${PATH%:}"
    export PATH
}

Aber ... warum nicht Gilles?

Gilles ' akzeptierte Antwort an anderer Stelle ist im allgemeinen Fall als "Shell Agnostic Idempotent Append" eindrucksvoll optimal . Im gemeinsamen Fall bashund zshmit keiner unerwünschten Symlinks jedoch erforderlich , die Leistungseinbuße so betrübt das zu tun Gentoo ricer in mir. Selbst bei unerwünschten Symlinks ist es fraglich, ob pro Subshell eine gegabelt wirdadd_to_PATH() Argument die mögliche Einfügung von Symlink-Duplikaten wert ist.

Für strenge Anwendungsfälle, bei denen sogar die Beseitigung von Symlink-Duplikaten gefordert ist, erfolgt dies in dieser zshspezifischen Variante über effiziente eingebaute und nicht über ineffiziente Gabeln:

+path.append() {
    local dirname
    for   dirname in "${@}"; do
        dirname="${dirname%/}"
        [[ "${dirname:0:1}" == '/' &&
           ":${PATH}:" != *":${dirname:A}:"* &&
           -d "${dirname}" ]] || continue
        PATH="${PATH}:${dirname}"
    done
    PATH="${PATH#:}"
    export PATH
}

Beachten Sie *":${dirname:A}:"*eher das als *":${dirname}:"*das Original. :Aist ein wundersamer zsh-ismus, der leider unter den meisten anderen muscheln fehlt - auch bash. Um zu zitieren man zshexpn:

A : Verwandeln Sie einen Dateinamen wie der aModifikator in einen absoluten Pfad und übergeben Sie das Ergebnis dann über die realpath(3)Bibliotheksfunktion, um symbolische Verknüpfungen aufzulösen. Hinweis: Auf Systemen ohne realpath(3)Bibliotheksfunktion werden symbolische Verknüpfungen nicht aufgelöst. Dies gilt auch für diese Systeme aund Aist äquivalent.

Keine weiteren Fragen.

Bitte. Viel Spaß beim sicheren Beschuss. Du hast es jetzt verdient.

Cecil Curry
quelle
0

Hier ist meine Version im funktionalen Programmierstil.

  • Funktioniert *PATHnicht nur für alle durch Doppelpunkte getrennten Variablen PATH.
  • Greift nicht auf den globalen Status zu
  • Funktioniert nur mit / an den angegebenen unveränderlichen Eingängen
  • Erzeugt eine einzelne Ausgabe
  • Keine Nebenwirkungen
  • Prinzipiell einprägsam

Bemerkenswert auch:

  • Agnostisch in Bezug auf exporting; das bleibt dem Anrufer überlassen (siehe Beispiele)
  • Rein bash; kein gabeln
path_add () {
  # $ 1: Zu prüfendes Element ist genau einmal in der angegebenen Pfadzeichenfolge enthalten
  # $ 2: Vorhandener Pfadzeichenfolgewert ("$ PATH", nicht "PATH")
  # $ 3 (optional, alles): Wenn angegeben, füge $ 1 hinzu; ansonsten voranstellen
  #
  # Beispiele:
  # $ export PATH = $ (path_add '/ opt / bin' "$ PATH")
  # $ CDPATH = $ (path_add '/ Music' "$ CDPATH" at_end)

  local -r already_present = "(^ |:) $ {1} ($ | :)"
  if [["$ 2" = ~ $ already_present]]; dann
    Echo "$ 2"
  elif [[$ # == 3]]; dann
    Echo "$ {2}: $ {1}"
  sonst
    Echo "$ {1}: $ {2}"
  fi
}
Phil Hudson
quelle
0

Mit diesem Skript können Sie am Ende hinzufügen $PATH:

PATH=path2; add_to_PATH after path1 path2:path3
echo $PATH
path2:path1:path3

Oder am Anfang hinzufügen $PATH:

PATH=path2; add_to_PATH before path1 path2:path3
echo $PATH
path1:path3:path2

# Add directories to $PATH iff they're not already there
# Append directories to $PATH by default
# Based on https://unix.stackexchange.com/a/4973/143394
# and https://unix.stackexchange.com/a/217629/143394
add_to_PATH () {
  local prepend  # Prepend to path if set
  local prefix   # Temporary prepended path
  local IFS      # Avoid restoring for added laziness

  case $1 in
    after)  shift;; # Default is to append
    before) prepend=true; shift;;
  esac

  for arg; do
    IFS=: # Split argument by path separator
    for dir in $arg; do
      # Canonicalise symbolic links
      dir=$({ cd -- "$dir" && { pwd -P || pwd; } } 2>/dev/null)
      if [ -z "$dir" ]; then continue; fi  # Skip non-existent directory
      case ":$PATH:" in
        *":$dir:"*) :;; # skip - already present
        *) if [ "$prepend" ]; then
           # ${prefix:+$prefix:} will expand to "" if $prefix is empty to avoid
           # starting with a ":".  Expansion is "$prefix:" if non-empty.
            prefix=${prefix+$prefix:}$dir
          else
            PATH=$PATH:$dir  # Append by default
          fi;;
      esac
    done
  done
  [ "$prepend" ] && [ "$prefix" != "" ] && PATH=$prefix:$PATH
}
Tom Hale
quelle