Могу ли я динамически изменять PATH на основе моего cwd?

Могу ли я динамически изменять PATH на основе моего cwd?

Я хотел бы изменить $PATHпеременную окружения в зависимости от моего текущего рабочего каталога.

Допустим, я нахожусь в /foo/bar/bazи у меня есть каталоги /foo/node_modules/.binи /foo/bar/baz/node_modules/.bin. Я хотел бы добавить все возможные ./node_modules/.binрекурсивно в $PATH.

Но когда я cdперехожу в другой каталог (например /foo/bar, ), я хочу, чтобы мой исходный, чистый файл $PATHбыл восстановлен, а затем снова начать ./node_modules/.binрекурсивный поиск.

(Я хочу решить свой собственный вопрос из системы отслеживания проблем npm:Можно ли также добавлять локально установленные пакеты в PATH?)

Примечание: у меня Mac, но меня интересует общее решение.

решение1

Введение

Если я правильно вас понял, вы хотите добавить любые каталоги, "$X/node_modules/.bin"где $Xнаходится $PWDили любой из его предков.

Скрипт в конце этого поста должен давать желаемое поведение. Вам нужно использовать его в каждом сеансе, где вы хотите. Если вы назовете файл augment_path.sh, то добавления этой строки в ваш .bashrcдолжно быть достаточно:

source augment_path.sh

Обсуждение

Я думаю, что у garyjohn основной подход правильный, но он ищет всех потомков, а не всех предков.

Переменная $PROMPT_COMMANDпозволяет указать команду, которая будет выполняться каждый раз при отображении приглашения. Я добавил переменную, чтобы разрешить восстановление $PROMPT_COMMAND_OLDоригинала$PROMPT_COMMAND

Это, возможно, не обязательно, но для хорошего тона я добавляю $LAST_WDпеременную и тест, чтобы избежать пересчета пути, когда каталог не изменился. Вы можете удалить все три строки, содержащие, LAST_WDесли хотите.

Функция augment_pathсканирует $PWDснизу вверх, ищет целевые каталоги в каждом предке и добавляет все найденные каталоги к пути.

  • Они размещаются в пути по порядку, поэтому самый глубокий такой каталог будет иметь приоритет, если возникнут какие-либо конфликты. Я предполагаю, что это желаемое поведение. Если нет, измените

    PATH_ADDITION="$PATH_ADDITION:$resolved_target"
    

    к

    PATH_ADDITION="$resolved_target:$PATH_ADDITION"
    
  • Однако все эти каталоги будут иметь приоритет над остальной частью пути. Если вы хотите, чтобы остальная часть пути имела приоритет, измените:

    PATH="$PATH_ADDITION:$RAW_PATH"
    

    к

    PATH="$RAW_PATH:$PATH_ADDITION"
    

Сценарий

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"

решение2

Вы можете работать с bashPROMPT_COMMAND:

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

Эта команда выполняется каждый раз, когда появляется приглашение. Таким образом, каждый раз, когда команда завершается, PATHпеременная изменяется. В конец добавляется дополнительный каталог.

Если в текущем рабочем каталоге нет каталога ./node_modules/.bin, путь все равно добавляется. Но это не проблема. Он не ищется (потому что его нет).

Смотрите демонстрацию:

$ 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

решение3

Я думаю, что включение в ваше завещание следующих слов ~/.bashrcсделает то, что вы хотите.

Если переменная среды bash PROMPT_COMMANDустановлена ​​на какую-либо команду, эта команда выполняется до того, как bash отобразит основную подсказку.

В коде ниже PROMPT_COMMANDзадано имя функции doit. Эта функция проверяет, изменился ли текущий рабочий каталог, и если да, то сначала устанавливает PATHего в исходное значение, а затем проверяет наличие подкаталога с именем node_modules/.bin. Если этот подкаталог существует, функция добавляет его имена и имена всех подкаталогов ниже его к 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

Связанный контент