我可以根据我的 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

讨论

我认为 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

你可以使用 bash 的PROMPT_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

相关内容