如何将目录添加到 PATH
原文由 Julia Evans 于 发布,订阅该博客
今天我和朋友聊了聊如何把一个目录添加到 PATH 这件事。对我来说,因为用了很久的终端,这件事感觉挺“理所当然”的,但当我去搜索具体操作步骤时,却发现根本找不到一篇把所有步骤都讲清楚的教程——很多教程只说“把这行加到 ~/.bashrc 里”,可如果你用的根本不是 bash 呢?如果你的 bash 配置其实在别的文件里呢?又该怎么搞清楚到底要添加哪个目录呢?
所以我想试着写一份更完整的步骤说明,顺便提一下这些年我踩过的坑。
第一步:你在用哪种 shell?
如果你不确定自己在用哪种 shell,可以这样来查看。运行:
ps -p $$ -o pid,comm=- 如果你用的是 bash,它会打印出
97295 bash - 如果你用的是 zsh,它会打印出
97295 zsh - 如果你用的是 fish,它会报错,类似 “In fish, please use $fish_pid”(
$$在 fish 里不是合法的语法,不过光看这条报错信息,你就知道自己在用 fish 了,而你大概本来就知道)
另外,bash 是 Linux 上的默认 shell,zsh 是 macOS 上的默认 shell(截至 2024 年)。这篇教程只会涉及 bash、zsh 和 fish。
第二步:找到 shell 的配置文件
- 在 zsh 里,通常是
~/.zshrc - 在 bash 里,可能是
~/.bashrc,但情况比较复杂,详见下一节的说明 - 在 fish 里,通常是
~/.config/fish/config.fish(如果你想百分百确定,可以运行echo $__fish_config_dir)
关于 bash 配置文件的说明
Bash 有三个可能的配置文件:~/.bashrc、~/.bash_profile 和 ~/.profile。
如果你不确定自己的系统用的是哪一个,我建议这样测试:
- 在
~/.bashrc里加上echo hi there - 重启终端
- 如果你看到了“hi there”,就说明正在使用
~/.bashrc!太好了! - 否则就把它删掉,再用
~/.bash_profile试同样的方法 - 如果前两个都不行,也可以试试
~/.profile。
(网上有很多复杂的流程图来解释 bash 是如何决定使用哪个配置文件的,但我觉得没必要去记这些,直接测试才是最快、最可靠的办法)
第三步:弄清楚要添加哪个目录
比如说,你想安装并运行一个叫 http-server 的程序,结果却不行,像这样:
$ npm install -g http-server
$ http-server
bash: http-server: command not found怎么找到 http-server 所在的目录呢?说实话,这通常不太容易——答案往往是“这取决于 npm 是怎么配置的”。这里有几个思路:
- 很多时候在初次设置新的安装工具(比如
cargo、npm、homebrew等)时,它会打印出如何更新 PATH 的说明。所以如果你当时留意了,就能直接拿到操作方法。 - 有时安装程序会自动帮你更新 shell 的配置文件,把
PATH改好 - 有时直接搜一下“where does npm install things?”就能找到答案
- 有些工具有子命令可以直接告诉你它们的安装位置,比如:
- Node/npm:
npm config get prefix(然后在后面加上/bin/) - Go:
go env GOPATH(然后在后面加上/bin/) - asdf:
asdf info | grep ASDF_DIR(然后在后面加上/bin/和/shims/)
- Node/npm:
第 3.1 步:确认目录是否正确
找到一个你觉得可能正确的目录后,一定要确认它真的对!比如,我发现自己机器上的 http-server 在 ~/.npm-global/bin 里。我可以通过直接运行该目录下的 http-server 来确认,像这样:
$ ~/.npm-global/bin/http-server
Starting up http-server, serving ./public成功了!现在你已经知道要把哪个目录添加到 PATH,我们进入下一步!
第四步:编辑 shell 配置文件
现在我们已经有了需要的两条关键信息:
- 你想添加到 PATH 的是哪个目录(比如
~/.npm-global/bin/) - 你的 shell 配置文件在哪(比如
~/.bashrc、~/.zshrc或~/.config/fish/config.fish)
接下来要添加的内容取决于你用的 shell:
bash 操作:
打开 shell 的配置文件,加入这样一行:
export PATH=$PATH:~/.npm-global/bin/(显然要把 ~/.npm-global/bin 换成你实际要添加的目录)
zsh 操作:
你可以像 bash 那样写,zsh 还提供了稍微花哨一点的语法,如果你喜欢的话:
path=(
$path
~/.npm-global/bin
)fish 操作:
在 fish 里,语法有所不同:
set PATH $PATH ~/.npm-global/bin(在 fish 里你也可以用 fish_add_path,相关说明在后文)
第五步:重启 shell
现在是非常重要的一步:更新了 shell 配置后,如果不重启,是不会生效的!
有两种办法:
- 打开一个新的终端(或终端标签页),最好把旧的关掉,免得弄混
- 运行
bash来启动一个新的 shell(如果用 zsh 就运行zsh,用 fish 就运行fish)
我发现这两种办法通常都没问题。
这样就应该完成了!试着再运行一下之前想运行的程序,应该就能用了。
如果还是不行,下面是一些可能会遇到的问题:
问题 1:运行了错误的程序
如果运行的是程序的错误版本,你可能需要把目录添加到 PATH 的开头,而不是末尾。
比如,在我的系统上装了两个版本的 python3,运行 which -a 就能看到:
$ which -a python3
/usr/bin/python3
/opt/homebrew/bin/python3shell 会使用列在最前面的那一个。
如果你想用 Homebrew 的版本,就需要把那个目录(/opt/homebrew/bin)添加到 PATH 的开头,在 shell 配置文件里这样写(是 /opt/homebrew/bin/:$PATH,而不是常用的 $PATH:/opt/homebrew/bin/)
export PATH=/opt/homebrew/bin/:$PATH在 fish 里则是:
set PATH ~/.cargo/bin $PATH问题 2:程序不是从 shell 中运行的
以上所有方法都只在你从 shell 中运行程序时才有效。如果你是从 IDE、图形界面、cron 定时任务或其他方式运行程序,就需要用别的方法把目录加到 PATH,具体细节取决于实际情况。
在 cron 定时任务中
一些可选方案:
- 使用要运行程序的完整路径,比如
/home/bork/bin/my-program - 在 crontab 的第一行写上你想要的完整 PATH(类似 PATH=/bin:/usr/bin:/usr/local/bin:…)。你可以在 shell 里运行
echo "PATH=$PATH"来获取当前使用的完整 PATH。
老实说,我已经很久没遇到在 IDE/图形界面中处理 PATH 的情况了,所以不太确定该怎么做,如果有人能指点,我会把相关说明补在这里。
问题 3:重复的 PATH 条目让调试更困难
如果你编辑了 PATH,然后通过运行 bash(或 zsh、fish)来启动新 shell,常常会出现重复的 PATH 条目,因为每次启动 shell 时,它都会往 PATH 里重复添加内容。
就我个人而言,我还没遇到过这种重复会导致什么故障的情况,但如果你想搞清楚 PATH 里到底有什么,重复项会让调试变得更麻烦。
有几种处理办法:
- 如果你在调试
PATH,就在全新的终端里进行,这样能得到“干净”的状态。应该可以避免重复。 - 在 shell 配置文件的末尾对
PATH去重(比如在 zsh 里据说可以用typeset -U path来实现) - 在添加目录时检查它是否已经在
PATH中(比如在 fish 里我记得可以用fish_add_path --path /some/directory来做)
如何给 PATH 去重取决于具体的 shell,而且并不总是有内置的方法,所以你需要去查一下在自己用的 shell 里该怎么做。
问题 4:更新 PATH 后丢失历史记录
在 bash 或 zsh 里,很容易遇到这种情况:
- 运行一条命令(失败了)
- 更新你的
PATH - 运行
bash来重新加载配置 - 按几下上方向键想重新运行那条失败的命令(或者打开一个新终端)
- 结果那条失败的命令不在历史记录里!为什么呢?
这是因为在 bash 中,默认情况下历史记录要等到退出 shell 时才会保存。
一些解决方法:
- 不要用运行
bash来重新加载配置,改为运行source ~/.bashrc(在 zsh 里是source ~/.zshrc)。这样会在当前会话中重新加载配置。 - 把 shell 配置为持续保存历史记录,而不是只在退出时才保存。(具体怎么做取决于你用的是 bash 还是 zsh,zsh 的历史记录选项有点复杂,我也不太确定最好的办法是什么)
关于 source 的说明
第一次安装 cargo(Rust 的安装工具)时,它会给出这样的 PATH 设置说明,里面完全没有提到具体的目录。
This is usually done by running one of the following (note the leading DOT):
. "$HOME/.cargo/env" # For sh/bash/zsh/ash/dash/pdksh
source "$HOME/.cargo/env.fish" # For fish它的思路是,你把那一行加到 shell 配置里,它们的脚本就会自动帮你设置好 PATH(以及其他一些东西)。
这很常见(比如 Homebrew 就建议你执行 brew shellenv),处理这个问题有两种方式:
- 直接按工具建议的做(比如把
. "$HOME/.cargo/env"加到 shell 配置里) - 弄清楚它们让你运行的脚本会往 PATH 里添加哪些目录,然后手动添加它们。我的做法是:
- 在我的 shell 里运行
. "$HOME/.cargo/env"(如果用 fish,就运行对应的 fish 版本) - 运行
echo "$PATH" | tr ':' '\n' | grep cargo来看看它添加了哪些目录 - 看到它显示
/Users/bork/.cargo/bin,然后简写为~/.cargo/bin - 把目录
~/.cargo/bin添加到 PATH(按本文中的步骤操作)
- 在我的 shell 里运行
我觉得按工具建议的做也没什么不对(那可能就是“最佳方式”!),但我个人通常会用第二种办法,因为我更想清楚地知道自己到底改了哪些配置。
关于 fish_add_path 的说明
fish 有一个很方便的函数叫 fish_add_path,可以像这样把目录添加到 PATH:
fish_add_path /some/directory这很酷(命令这么简单!),但我已经不再用它了,原因有几个:
- 有时候
fish_add_path会用“通用变量”的方式为今后所有会话更新PATH,有时候又只为当前会话更新,很难判断它到底会怎么做。理论上文档里有解释,但我没看懂。 - 如果你几周或几个月后因为弄错了,需要把某个目录从
PATH中移除,会有点麻烦(不过这个 GitHub issue 的评论里有操作方法)。
就这些
希望这能帮到一些人。如果你把目录添加到 PATH 时还遇到过其他大坑,或者对本文有疑问,欢迎在 Mastodon 或 Bluesky 上告诉我!
随机一篇博客
评论
登录后参与讨论