我可以根據我的 cwd 動態更改我的 PATH 嗎?

我可以根據我的 cwd 動態更改我的 PATH 嗎?

我想$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

討論

我認為加里約翰的基本方法是正確的,但他正在搜索所有後代而不是所有祖先。

$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

相關內容