Eine bequemere Möglichkeit, einen langen $ PATH zu bearbeiten?

35

Ich möchte in ~ / .bashrc ein paar Verzeichnisse zu meinem $ PATH hinzufügen.

Mein $ PATH ist ziemlich lang, daher ist es etwas schwierig zu erkennen, welche Verzeichnisse es enthält und in welcher Reihenfolge.

Ich weiß, dass ich mein ~ / .bashrc so ändern kann, dass es:

PATH=$PATH:/some/dir
PATH=$PATH:/another/dir:/yet/another
PATH=$PATH:/and/another
...

es würde das Lesen erleichtern. Aber ich habe mich gefragt, ob Bash in den letzten Jahren eine Syntax entwickelt hat, die es einfacher macht, einen langen Pfad anzugeben. Ich fantasiere zum Beispiel von einer Syntax ähnlich der folgenden:

PATH=:((
  /some/dir
  /another/dir
  /yet/another
  /and/another
  ...
))

Ich weiß, dass eine solche Syntax ungültig ist. Ich habe mich gefragt, ob es etwas gibt, das so einfach ist. Gibt es?

Niccolo M.
quelle
Das traditionelle Tutorial zum Festlegen des Pfades über PATH=foo:$PATHscheint falsch, weil es jedes Mal das Wachstum beibehält source ~/.bashrcund sogar exec bashnicht anders kann, als es $PATHist export.
15.

Antworten:

25

Ich verwende eine Reihe von praktischen Funktionen, um einer Variablen einen Pfad voranzustellen oder anzufügen. Die Funktionen befinden sich im Distribution-Tarball für Bash in einer Contrib-Datei mit dem Namen "pathfuncs".

  • add_path fügt den Eintrag am Ende der PATH-Variablen ein
  • pre_path fügt den Eintrag am Anfang der PATH-Variablen ein
  • del_path entfernt den Eintrag aus der PATH-Variablen, wo immer er sich befindet

Wenn Sie als zweites Argument eine Variable angeben, wird diese anstelle von PATH verwendet.

Der Einfachheit halber sind sie hier:

# is $1 missing from $2 (or PATH) ?
no_path() {
    eval "case :\$${2-PATH}: in *:$1:*) return 1;; *) return 0;; esac"
}
# if $1 exists and is not in path, append it
add_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1"
}
# if $1 exists and is not in path, prepend it
pre_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}"
}
# if $1 is in path, remove it
del_path () {
  no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: |
    sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`
}

Wenn Sie diese zu Ihrer Bash-Startdatei hinzufügen, können Sie Ihren PATH folgendermaßen hinzufügen:

pre_path $HOME/bin
add_path /sbin
add_path /usr/sbin

Oder geben Sie eine andere Variable an:

pre_path $HOME/man MANPATH
pre_path $HOME/share/man MANPATH
add_path /usr/local/man MANPATH
add_path /usr/share/man MANPATH

Ich verwende diese Methode in meinen rc-Dateien, indem ich die pre_paths als erstes und die add_paths als zweites setze. Dadurch sind alle meine Pfadänderungen auf einen Blick leicht zu verstehen. Ein weiterer Vorteil ist, dass die Zeilen so kurz sind, dass ich bei Bedarf einen abschließenden Kommentar zu einer Zeile hinzufügen kann.

Und da es sich um Funktionen handelt, können Sie diese interaktiv über die Befehlszeile verwenden, indem add_path $(pwd)Sie beispielsweise das aktuelle Verzeichnis zum Pfad hinzufügen.

Seestern
quelle
Vielen Dank. Ich habe Ihren Code überprüft und es funktioniert. Überraschenderweise habe ich auch eine Verwendung für del_path gefunden (A "." Kriecht in einigen Situationen in meinen PATH, Teufel weiß woher, also habe ich es getan del_path .).
Niccolo M.
Hallo. es ist möglich, diese pathfuncs aus dem bashrc-script zu beziehen (einzuschließen) oder sollte ich sie kopieren / einfügen?
Cristiano
@ Cristiano Entweder funktioniert. Es liegt wirklich an dir.
Starfish
11

OK, ich habe die folgende Lösung gefunden, die ich für elegant halte (was die Shell-Syntax angeht). Es verwendet die Array-Syntax von Bash und bietet auch eine übersichtliche Möglichkeit , Elemente zu verbinden:

paths=(
  /some/dir
  /another/dir
  '/another/dir with spaces in it'
  /yet/another
  /and/another
  /end
)
paths_joined=$( IFS=: ; echo "${paths[*]}" )

PATH=$paths_joined:$PATH

WARNEN!

Es stellt sich heraus, dass diese Lösung ein Problem hat : Im Gegensatz zu den Lösungen von @terdon und @Starfish wird nicht zuerst geprüft, ob sich die Pfade bereits in PATH befinden. Da ich diesen Code also in ~ / .bashrc (und nicht in ~ / .profile) einfügen möchte, werden sich doppelte Pfade in PATH einschleichen. Verwenden Sie diese Lösung also nicht (es sei denn, Sie fügen sie in ~ / .profile ein (oder besser ~ / .bash_profile, da sie eine Bash-spezifische Syntax hat).

Niccolo M.
quelle
1
Sehr schön. Können Sie bitte eine Antwort akzeptieren, damit andere nicht hierher kommen, um eine Lösung anzubieten, wenn Sie bereits eine gefunden haben? Danke
Grund
Doppelte Pfade sind eigentlich kein Problem. Es ist sehr unwahrscheinlich, dass Sie genügend Verzeichnisse hinzugefügt haben, PATHum tatsächlich Leistungsprobleme zu verursachen (insbesondere, da die Shell erfolgreiche Suchvorgänge im Cache speichert).
Chepner
5

Ich benutze die unten stehende Funktion in meinem ~/.bashrc. Es ist etwas, das ich von einem Sysadmin in meinem alten Labor bekommen habe, aber ich glaube nicht, dass er sie geschrieben hat. Fügen Sie einfach diese Zeilen zu Ihrem ~/.profileoder ~/.bashrc:

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

Dies hat verschiedene Vorteile:

  • Das Hinzufügen neuer Verzeichnisse zum $PATHist trivial pathmunge /foo/bar:;
  • Es werden doppelte Einträge vermieden.
  • Sie können wählen, ob Sie einen neuen Eintrag am Anfang ( pathmunge /foo/baroder am Ende ( pathmunge /foo/barnach) des $PATH.

Die Initialisierungsdatei Ihrer Shell würde dann etwa Folgendes enthalten:

pathmunge /some/dir
pathmunge /another/dir
pathmunge '/another/dir with spaces in it'
pathmunge /yet/another
pathmunge /and/another
pathmunge /end
terdon
quelle
Vielen Dank. Aber ich werde @ Starfishs Lösung auswählen, weil seine nicht laicht grep.
Niccolo M.
2
@NiccoloM. Kein Problem, nehmen Sie an, was immer Sie bevorzugen. Seien Sie jedoch vorsichtig mit der Vorgehensweise von Starfish, da dieser beliebigen Code ausführt, evalsodass Sie ernsthaften Schaden anrichten können, wenn Sie ihn mit dem falschen Argument ausführen.
Terdon
Beachten Sie, dass schnellere Funktion besteht in Red Hat zu tun , dass ohne externe Befehl grep, siehe bugzilla.redhat.com/show_bug.cgi?id=544652#c7
林果皞
4

Ich möchte in ~ / .bashrc ein paar Verzeichnisse zu meinem $ PATH hinzufügen.

Ich benutze folgendes in Cygwin. Es sollte in anderen Versionen von Bash funktionieren. Sie können das entfernen unset PATH, um auf Ihrem aktuellen aufzubauen PATH(wenn Sie dies tun, müssen Sie möglicherweise herausfinden, wie Sie die :Trennzeichen korrekt hinzufügen ).

Hinweis:

  • Ich hatte diese Funktionalität einmal in einer bashFunktion, verlor sie aber nach einem Festplattenabsturz.

In meinem .bash_profile:

# Build up the path using the directories in ~/.path_elements
unset PATH
while read line; do 
  PATH="${PATH}$line"; 
done < ~/.path_elements

...

# Add current directory to path
export PATH=".:${PATH}"

In ~/.path_elements:

/home/DavidPostill/bin:
/usr/local/bin:
/usr/bin:
/c/Windows/system32:
/c/Windows
DavidPostill
quelle
Vielen Dank. Ihre Antwort hat mich dazu inspiriert, an einer ähnlichen Lösung zu arbeiten. (Nach meinem Geschmack ist es eine Mühe, die Pfade in einer separaten Datei zu speichern.)
Niccolo M.
1

Ich verwende dies in meinem .bashrc (und auch in meinem .zshrc, da ich normalerweise zsh verwende, sofern verfügbar, anstelle von bash). Zugegeben, es erfordert das manuelle Hinzufügen von Verzeichnissen. Ein Vorteil ist jedoch, dass ich beim Aktualisieren weiterhin auf neue Server kopieren kann und mir keine Sorgen machen muss, dass der Pfad auf einem neuen Server mit Verzeichnissen erstellt wird, die dort nicht vorhanden sind.

##
## PATH
##
## Anstatt nur unseren PATH mit Verzeichnissen zu überhäufen, die das können
## ist für diesen Server nicht geeignet, versuchen Sie intelligent zu sein, was wir hinzufügen
##
PATH = / usr / local / sbin: / usr / local / bin: / usr / sbin: / usr / bin: / sbin: / bin
[-d / cs / sbin] && PATH = / cs / sbin: $ PATH
[-d / cs / bin] && PATH = / cs / bin: $ PATH
[-d / usr / ucb] && PATH = $ PATH: / usr / ucb
[-d / usr / ccs / bin] && PATH = $ PATH: / usr / ccs / bin
[-d / usr / local / ssl / bin] && PATH = $ PATH: / usr / local / ssl / bin
[-d / usr / krb5 / bin] && PATH = $ PATH: / usr / krb5 / bin
[-d / usr / krb5 / sbin] && PATH = $ PATH: / usr / krb5 / sbin
[-d / usr / kerberos / sbin] && PATH = $ PATH: / usr / kerberos / sbin
[-d / usr / kerberos / bin] && PATH = $ PATH: / usr / kerberos / bin
[-d /cs/local/jdk1.5.0/bin] && PATH = $ PATH: /cs/local/jdk1.5.0/bin
[-d /usr/java/jre1.5.0_02/bin] && PATH = $ PATH: /usr/java/jre1.5.0_02/man
[-d /usr/java1.2/bin] && PATH = $ PATH: /usr/java1.2/bin
[-d /cs/local/perl5.8.0/bin] && PATH = $ PATH: /cs/local/perl5.8.0/bin
[-d / usr / perl5 / bin] && PATH = $ PATH: / usr / perl5 / bin
[-d / usr / X11R6 / bin] && PATH = $ PATH: / usr / X11R6 / bin
[-d / etc / X11] && PATH = $ PATH: / etc / X11
[-d / opt / sfw / bin] && PATH = $ PATH: / opt / sfw / bin
[-d / usr / local / apache / bin] && PATH = $ PATH: / usr / local / apache / bin
[-d / usr / apache / bin] && PATH = $ PATH: / usr / apache / bin
[-d / cs / admin / bin] && PATH = $ PATH: / cs / admin / bin
[-d / usr / openwin / bin] && PATH = $ PATH: / usr / openwin / bin
[-d / usr / xpg4 / bin] && PATH = $ PATH: / usr / xpg4 / bin
[-d / usr / dt / bin] && PATH = $ PATH: / usr / dt / bin

Ich mache dasselbe für meinen MANPATH:

##
## MANPATH
##
## Anstatt nur unseren MANPATH mit Verzeichnissen zu überhäufen, die das können
## ist für diesen Server nicht geeignet, versuchen Sie intelligent zu sein, was wir hinzufügen
##
MANPATH = / usr / local / man
[-d / usr / share / man] && MANPATH = $ MANPATH: / usr / share / man
[-d / usr / local / share / man] && MANPATH = $ MANPATH: / usr / local / share / man
[-d / usr / man] && MANPATH = $ MANPATH: / usr / man
[-d / cs / man] && MANPATH = $ MANPATH: / cs / man
[-d / usr / krb5 / man] && MANPATH = $ MANPATH: / usr / krb5 / man
[-d / usr / kerberos / man] && MANPATH = $ MANPATH: / usr / kerberos / man
[-d / usr / local / ssl / man] && MANPATH = $ MANPATH: / usr / local / ssl / man
[-d /cs/local/jdk1.5.0/man] && MANPATH = $ MANPATH: /cs/local/jdk1.5.0/man
[-d /usr/java/jre1.5.0_02/man] && MANPATH = $ MANPATH: /usr/java/jre1.5.0_02/man
[-d /usr/java1.2/man] && MANPATH = $ MANPATH: /usr/java1.2/man
[-d / usr / X11R6 / man] && MANPATH = $ MANPATH: / usr / X11R6 / man
[-d / usr / local / apache / man] && MANPATH = $ MANPATH: / usr / local / apache / man
[-d / usr / local / mysql / man] && MANPATH = $ MANPATH: / usr / local / mysql / man
[-d /cs/local/perl5.8.0/man] && MANPATH = $ MANPATH: /cs/local/perl5.8.0/man
[-d / usr / perl5 / man] && MANPATH = $ MANPATH: / usr / perl5 / man
[-d / usr / local / perl / man] && MANPATH = $ MANPATH: / usr / local / perl / man
[-d /usr/local/perl5.8.0/man] && MANPATH = $ MANPATH: /usr/local/perl5.8.0/man
[-d / usr / openwin / man] && MANPATH = $ MANPATH: / usr / openwin / man

Dieser Ansatz verfügt nicht nur über eine einzelne Datei, die ich auf Systeme in unterschiedlichen Umgebungen kopieren kann, ohne befürchten zu müssen, dass nicht vorhandene Verzeichnisse zum PATH hinzugefügt werden, sondern bietet auch den Vorteil, dass ich die Reihenfolge festlegen kann, in der Verzeichnisse im PATH angezeigt werden sollen. Da die erste Zeile jeder Definition die PATH-Variable vollständig neu definiert, kann ich meine .bashrc-Datei aktualisieren und nach der Bearbeitung als Quelle verwenden, damit meine Shell aktualisiert wird, ohne doppelte Einträge hinzuzufügen (was ich vor langer Zeit erlebt habe, als ich einfach mit " $ PATH = $ PATH: / new / dir ". Dadurch wird sichergestellt, dass ich eine saubere Kopie in der von mir gewünschten Reihenfolge erhalte.

Brian Snook
quelle
1
Vorschlag für eine Alternative: d="/usr/share/man" ; [ -d "$d" ] && MANPATH="$MANPATH:${d}"Es ist kürzer und einfacher, ein neues Verzeichnis hinzuzufügen (kopieren Sie einfach eine Zeile und bearbeiten Sie den ersten Teil "d = ...."). Für den PATH-Befehl denke ich jedoch, dass Sie viel zu viele Verzeichnisse in Ihrem PATH haben werden, was nicht immer gut ist (was ist, wenn auf einem der weniger bekannten Pfade ein "foo" -Befehl vorhanden ist und Sie etwas ganz anderes tun das, was ein normaler Benutzer erwarten würde?)
Olivier Dulac
Das OP forderte eine knappe Möglichkeit zum Hinzufügen von Pfaden. Dies ist weitaus ausführlicher und fehleranfälliger als das, was sie bereits zu vermeiden versuchten.
Underscore_d
-1

Es gibt einen einfachen Weg! Lesen Sie Shell-Funktionen und Pfadvariablen im Linux Journal , 1. März 2000 Von Stephen Collyer

Mit den Funktionen kann ich in meiner Bash-Umgebung einen neuen Datentyp verwenden - die durch Doppelpunkte getrennte Liste. Zusätzlich zu PATH verwende ich sie zum Anpassen von LOCATE_PATH, MANPATH und anderen sowie als allgemeinen Datentyp in der Bash-Programmierung. So richte ich meinen PATH ein (mithilfe der Funktionen):

# Add my bin directory to $PATH at the beginning, so it overrides 
addpath -f -p PATH $HOME/bin

# For Raspberry Pi development (add at end)
addpath -b -p PATH ${HOME}/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin

# remove nonexistent directories from PATH
delpath -n -p PATH

# ensure PATH contains unique entries
uniqpath -p PATH

Da der Link zum Linux-Journal als "defekt" bezeichnet wird, habe ich die Bash-Pfadfunktionen in einer .shar-Datei unter http://pastebin.ubuntu.com/13299528/ abgelegt.

Waltinator
quelle
3
Welche Funktionen sind das? Wie benutzt das OP sie? Vermutlich sind sie in Ihrer Antwort in dem defekten Link beschrieben, weshalb wir immer wollen, dass Antworten in sich geschlossen sind. Bitte bearbeite und beziehe die aktuellen Funktionen in deine Antwort ein.
Terdon
@terdon: Link funktioniert bei mir, ich habe eine .shar-Datei im Pastebin abgelegt, ich werde hier keine 1K-Zeilen posten.
Waltinator
Hoppla, arbeitet jetzt auch für mich. Das tat es nicht, als ich den Kommentar hinterließ. Der Kern meines Kommentars war jedenfalls, dass wir versuchen, in Antworten nicht auf externe Ressourcen zu verweisen. Wir möchten, dass die Informationen hier gespeichert werden , wo sie aktualisiert, bearbeitet und unempfindlich gegen Links sind.
Terdon