Kann ich meinen PATH dynamisch basierend auf meinem cwd ändern?

Kann ich meinen PATH dynamisch basierend auf meinem cwd ändern?

Ich möchte meine $PATHUmgebungsvariable abhängig von meinem aktuellen Arbeitsverzeichnis ändern.

Angenommen, ich bin in /foo/bar/bazund habe die Verzeichnisse /foo/node_modules/.binund /foo/bar/baz/node_modules/.bin. Ich möchte alle möglichen ./node_modules/.binrekursiv zu hinzufügen $PATH.

Aber wenn ich cdin ein anderes Verzeichnis (wie /foo/bar) gehe, möchte ich mein ursprüngliches, sauberes Verzeichnis $PATHwiederherstellen und dann ./node_modules/.binerneut mit der rekursiven Suche beginnen.

(Ich möchte meine eigene Frage aus dem Issue-Tracker von npm lösen:Können wir auch lokal installierte Pakete zu PATH hinzufügen?)

Hinweis: Ich verwende einen Mac, bin aber an einer allgemeinen Lösung interessiert.

Antwort1

Einführung

Wenn ich Sie richtig verstehe, möchten Sie alle Verzeichnisse hinzufügen, "$X/node_modules/.bin"in denen $Xsich das $PWDoder eines seiner Vorgänger befindet.

Das Skript am Ende dieses Beitrags sollte das gewünschte Verhalten liefern. Sie müssen es in jeder Sitzung, in der Sie es wünschen, als Quelle verwenden. Wenn Sie die Datei benennen , sollte es ausreichen, augment_path.shdiese Zeile hinzuzufügen :.bashrc

source augment_path.sh

Diskussion

Ich denke, Garyjohn hat den grundsätzlichen Ansatz richtig, aber er durchsucht alle Nachkommen und nicht alle Vorfahren.

Mit der $PROMPT_COMMANDVariable können Sie einen Befehl angeben, der jedes Mal ausgeführt werden soll, wenn die Eingabeaufforderung angezeigt wird. Ich habe eine $PROMPT_COMMAND_OLDVariable hinzugefügt, mit der das Original $PROMPT_COMMANDwiederhergestellt werden kann

Es ist wahrscheinlich nicht notwendig, aber der guten Form halber füge ich eine $LAST_WDVariable hinzu und teste, um eine Neuberechnung des Pfads zu vermeiden, wenn sich das Verzeichnis nicht geändert hat. Sie können alle drei Zeilen entfernen, die enthalten, LAST_WDwenn Sie möchten.

Die augment_pathFunktion scannt von $PWDoben, sucht in jedem Vorgänger nach den Zielverzeichnissen und fügt alle gefundenen Verzeichnisse dem Pfad hinzu.

  • Sie werden in der Reihenfolge im Pfad platziert, so dass das tiefste Verzeichnis Vorrang hat, wenn es Konflikte gibt. Ich gehe davon aus, dass dies das gewünschte Verhalten ist. Wenn nicht, ändern Sie

    PATH_ADDITION="$PATH_ADDITION:$resolved_target"
    

    Zu

    PATH_ADDITION="$resolved_target:$PATH_ADDITION"
    
  • Diese Verzeichnisse haben jedoch alle Vorrang vor dem Rest des Pfads. Wenn Sie möchten, dass der Rest des Pfads Vorrang hat, ändern Sie Folgendes:

    PATH="$PATH_ADDITION:$RAW_PATH"
    

    Zu

    PATH="$RAW_PATH:$PATH_ADDITION"
    

Skript

RAW_PATH="$PATH"
LAST_WD=`pwd`

augment_path() {
    target="node_modules/.bin"
    if [ "$PWD" = "$LAST_WD" ]; then return 0; fi;
    PATH_ADDITION=""
    scandir="$PWD"
    until [ "$scandir" = "" ]; do
    resolved_target="$scandir"/"$target"
    if [ -d "$resolved_target" ]; then
        PATH_ADDITION="$PATH_ADDITION:$resolved_target"
    fi
    scandir="${scandir%/*}"
    done 
    PATH="$PATH_ADDITION:$RAW_PATH"
    LAST_WD=`pwd`
}

PROMPT_COMMAND_OLD="${PROMPT_COMMAND%; augment_path}"
PROMPT_COMMAND="$PROMPT_COMMAND_OLD; augment_path"

Antwort2

Sie können mit Bash's arbeitenPROMPT_COMMAND:

PROMPT_COMMAND='[ -z "$X" ] && X=$PATH; PATH=$X:$(pwd)/node_modules/.bin'

Dieser Befehl wird jedes Mal ausgeführt, wenn die Eingabeaufforderung angezeigt wird. Daher PATHwird die Variable jedes Mal geändert, wenn ein Befehl beendet wird. Am Ende wird ein zusätzliches Verzeichnis hinzugefügt.

./node_modules/.binWenn im aktuellen Arbeitsverzeichnis kein Verzeichnis vorhanden ist , wird der Pfad trotzdem angehängt. Das ist aber kein Problem. Es wird nicht danach gesucht (da es nicht existiert).

Sehen Sie sich die Demonstration an:

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$ PROMPT_COMMAND='[ -z "$X" ] && X=$PATH; PATH=$X:$(pwd)/node_modules/.bin'
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/node_modules/.bin
$ cd test
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/test/node_modules/.bin
$ cd /etc/
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/etc/node_modules/.bin

Antwort3

Ich denke, wenn Sie das Folgende in Ihre Datei aufnehmen, ~/.bashrcwird das erreicht, was Sie möchten.

Wenn die Umgebungsvariable von Bash PROMPT_COMMANDauf einen bestimmten Befehl eingestellt ist, wird dieser Befehl ausgeführt, bevor Bash die primäre Eingabeaufforderung anzeigt.

Im folgenden Code PROMPT_COMMANDwird auf den Namen einer Funktion gesetzt, doit. Diese Funktion prüft, ob sich das aktuelle Arbeitsverzeichnis geändert hat. Wenn ja, wird es zunächst PATHauf den ursprünglichen Wert gesetzt. Anschließend wird geprüft, ob ein Unterverzeichnis mit dem Namen node_modules/.binvorhanden ist. Wenn dieses Unterverzeichnis vorhanden ist, hängt die Funktion den Namen dieses Unterverzeichnisses und aller Unterverzeichnisse darunter an PATH.

orig_path="$PATH"
prev_pwd=""
doit() {
    if [ "$PWD" != "$prev_pwd" ]; then
        PATH="$orig_path"
        if [ -d "node_modules/.bin" ]; then
            PATH="$PATH$(find $PWD/node_modules/.bin -type d -printf ':%p')"
            # For POSIX compatibility (macOS or other), use:
            # PATH="$PATH$(find $PWD/node_modules/.bin -type d -exec echo -n :{} \;)"
        fi
        prev_pwd="$PWD"
    fi
}
PROMPT_COMMAND=doit

verwandte Informationen