How to add a directory to your PATH

Julia Evans

如何将目录添加到 PATH

今天我和一位朋友聊了如何将目录添加到 PATH。这件事对我来说感觉“理所当然”,因为我已经使用终端很久了,但当我搜索相关操作说明时,却发现没有一篇能把所有步骤都讲清楚——很多教程只说“把这行加到 ~/.bashrc”,但如果你用的不是 bash 呢?如果你的 bash 配置其实在另一个文件里呢?你又该如何弄清楚到底要添加哪个目录呢?

所以我想试着写一份更完整的说明,并提及这些年里我遇到的一些坑。

第 1 步:你在用哪种 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 是 Mac OS 上的默认 shell(截至 2024 年)。本说明只会涉及 bash、zsh 和 fish。

第 2 步:找到你的 shell 配置文件

  • 在 zsh 中,通常是 ~/.zshrc
  • 在 bash 中,可能是 ~/.bashrc,但情况比较复杂,见下一节的说明
  • 在 fish 中,通常是 ~/.config/fish/config.fish(如果你想百分之百确定,可以运行 echo $__fish_config_dir

关于 bash 配置文件的说明

Bash 有三个可能的配置文件:~/.bashrc~/.bash_profile~/.profile

如果你不确定自己的系统设置为使用哪一个,我建议用这种方法来测试:

  1. 往你的 ~/.bashrc 里添加 echo hi there
  2. 重启你的终端
  3. 如果你看到了“hi there”,就说明正在使用 ~/.bashrc!太好了!
  4. 否则就把它删掉,然后用 ~/.bash_profile 尝试同样的做法
  5. 如果前两个都不行,你也可以试试 ~/.profile

(网上有很多复杂的流程图解释 bash 如何决定使用哪个配置文件,但依我看没必要去死记它们,直接测试是最快、最可靠的方法)

第 3 步:弄清楚要添加哪个目录

假设你正尝试安装并运行一个叫 http-server 的程序,却遇到了这样的情况:

$ npm install -g http-server
$ http-server
bash: http-server: command not found

怎么找到 http-server 在哪个目录下呢?老实说,这通常并不那么容易——答案往往是“这取决于 npm 是怎么配置的”。这里有几个思路:

  • 通常在设置一个新的安装工具(如 cargonpmhomebrew 等)时,首次安装时会打印出如何更新 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/

第 3.1 步:再次确认是正确的目录

一旦你找到了一个可能是正确目录的位置,一定要确认它确实是对的!例如,我发现在我的机器上,http-server 位于 ~/.npm-global/bin。我可以通过像这样直接运行该目录下的 http-server 程序来确认:

$ ~/.npm-global/bin/http-server
Starting up http-server, serving ./public

成功了!现在你已经知道需要添加到 PATH 的目录是哪个,让我们进入下一步!

第 4 步:编辑你的 shell 配置

现在我们已经有了所需的两条关键信息:

  1. 你想添加到 PATH 的目录是哪个(比如 ~/.npm-global/bin/
  2. 你的 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,相关说明见下文

第 5 步:重启你的 shell

现在是非常重要的一步:如果你不重启 shell,更新配置文件是不会生效的!

有两种方法可以做到:

  1. 打开一个新的终端(或终端标签页),最好把旧的关掉以免混淆
  2. 运行 bash 来启动一个新的 shell(如果你用的是 zsh 就运行 zsh,用 fish 就运行 fish

我发现这两种方法通常都能正常工作。

这样就应该完成了!试着运行你之前想运行的程序,现在应该可以正常工作了。

如果还是不行,这里列出了你可能会遇到的几个问题:

问题 1:运行了错误的程序

如果运行的是错误的版本的程序,你可能需要把目录添加到 PATH 的开头而不是末尾。

例如,在我的系统上安装了两个版本的 python3,我可以通过运行 which -a 看到:

$ which -a python3
/usr/bin/python3
/opt/homebrew/bin/python3

你的 shell 会使用排在最前面的那一个

如果你想使用 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
  • 将你想要的完整 PATH 作为 crontab 的第一行(类似 PATH=/bin:/usr/bin:/usr/local/bin:……)。你可以通过在 shell 中运行 echo "PATH=$PATH" 来获取当前使用的完整 PATH。

老实说,我不太确定在 IDE/图形界面中该如何处理,因为我已经很久没有遇到这种情况了,如果有人能指点正确的做法,我会在这里补充说明。

问题 3:重复的 PATH 条目让调试更困难

如果你编辑了 path 并通过运行 bash(或 zshfish)来启动新 shell,你常常会得到重复的 PATH 条目,因为 shell 每次启动时都会不断往你的 PATH 里添加内容。

就我个人而言,我还没遇到过这种重复会导致什么故障的情况,但重复项会让你在试图弄清 PATH 内容时更难调试。

一些处理方法:

  1. 如果你在调试 PATH,就在一个全新的终端里进行,这样能得到一个“干净”的初始状态,可以避免重复。
  2. 在 shell 配置的末尾对 PATH 去重(例如在 zsh 中据说可以用 typeset -U path 来实现)
  3. 在添加目录时检查它是否已经在 PATH 中(例如在 fish 中我认为可以用 fish_add_path --path /some/directory 来实现)

如何对 PATH 去重取决于具体的 shell,而且并不总是有内置的方法,所以你需要查找在你的 shell 中如何实现。

问题 4:更新 PATH 后丢失历史记录

在 bash 或 zsh 中很容易遇到这种情况:

  1. 运行一条命令(失败了)
  2. 更新你的 PATH
  3. 运行 bash 来重新加载配置
  4. 按几次上箭头想重新运行失败的命令(或打开一个新终端)
  5. 失败的命令不在历史记录里了!为什么?

这是因为在 bash 中,默认情况下历史记录要等到你退出 shell 时才会保存。

一些修复选项:

  • 不要运行 bash 来重新加载配置,而是运行 source ~/.bashrc(在 zsh 中则是 source ~/.zshrc)。这会在当前会话内重新加载配置。
  • 将你的 shell 配置为持续保存历史记录,而不是仅在 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),有两种处理方式:

  1. 就按工具建议的去做(比如把 . "$HOME/.cargo/env" 加到你的 shell 配置中)
  2. 弄清楚它们让你运行的脚本会往 PATH 里添加哪些目录,然后手动添加它们。我的做法是:
    • 在我的 shell 中运行 . "$HOME/.cargo/env"(如果用 fish 就运行 fish 版本)
    • 运行 echo "$PATH" | tr ':' '\n' | grep cargo 来找出它添加了哪些目录
    • 看到它显示 /Users/bork/.cargo/bin,并将其简化为 ~/.cargo/bin
    • 将目录 ~/.cargo/bin 添加到 PATH(按照本文中的说明)

我觉得按工具建议的去做也没什么问题(那可能是“最佳方式”!),但我个人通常会用第二种方法,因为我更喜欢清楚地知道自己到底改了哪些配置。

关于 fish_add_path 的说明

fish 有一个很方便的函数叫 fish_add_path,你可以像这样用它来把目录添加到 PATH

fish_add_path /some/directory

这很酷(命令如此简单!),但我已经不再使用它,原因有两点:

  1. 有时 fish_add_path 会为之后的所有会话更新 PATH(通过“通用变量”),有时又只为当前会话更新 PATH,我很难判断它到底会执行哪一种。理论上文档对此有解释,但我没能看懂。
  2. 如果你在几周或几个月后因为可能弄错了而需要从 PATH移除该目录,会有点麻烦(不过这个 GitHub issue 的评论中有相关说明)。

就是这些

希望这能帮到一些人。如果你在将目录添加到 PATH 时遇到过其他主要的坑,或者对本文有疑问,请告诉我(在 Mastodon 或 Bluesky 上)!

原文由 Julia Evans 发布

本文章由 muse-spark-1.2-contributor 进行翻译