理解 Linux 中的 $PATH 变量
在 Linux 系统后台运行着一个名为 $PATH 的重要变量,它默默地影响着用户体验。虽然它在幕后操作,但它的作用是清晰且有益的。本文将深入探讨 $PATH 的功能及其配置方法。
什么是 Linux 上的 $PATH?它是如何运作的?
当您在终端输入命令并按下回车键时,系统会立即启动一系列活动,甚至在命令本身执行之前。
Bash 是大多数 Linux 发行版的默认 shell。它负责解析您输入的文本,识别参数、管道和重定向等元素。之后,它会查找与命令对应的可执行二进制文件,并使用您提供的参数启动它们。
Shell 确定是否需要查找二进制文件的第一步是判断所用命令是否是 shell 内建命令。如果命令是 shell 的“内建命令”,则无需进一步搜索。
Shell 内建命令是最容易找到的,因为它们是 shell 自身的一部分。这就像把它们放在工具箱里,它们总是随手可得。
然而,如果您需要使用其他工具,您就必须在工作间里寻找。它是在工作台上还是在墙上的挂钩上?这就是 $PATH 环境变量的用途。它包含一个 shell 搜索可执行文件的目录列表,以及搜索这些目录的顺序。
您可以使用 type 命令来检查一个命令是 shell 内建命令、别名、函数还是独立的二进制文件,例如:
type clear
type cd
这表明 clear 是一个二进制文件,它的第一个匹配项位于 /usr/bin 目录下。您的计算机上可能安装了多个 clear 版本,但 shell 会优先使用这个版本。
不出所料,cd 是一个 shell 内建命令。
查看你的 $PATH
您可以很容易地查看 $PATH 变量的内容。只需使用 echo 命令,并打印 $PATH 变量中存储的值:
echo $PATH
输出结果是一系列以冒号 (:) 分隔的文件系统路径。Shell 会从左到右依次搜索这些路径,查找与您输入的命令匹配的可执行文件。
我们可以按顺序列出 $PATH 中包含的目录,以及它们的搜索顺序:
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin
需要注意的是,搜索并非从当前工作目录开始,而是按照 $PATH 中列出的目录顺序进行。
如果当前工作目录不在您的 $PATH 中,则 shell 不会在其中搜索可执行文件。同样,如果命令存储在 $PATH 中未列出的目录里,shell 也找不到它们。
为了验证这一点,我们创建了一个名为 rf 的小程序。当它被执行时,它会在终端窗口中打印出启动它的目录名称。该程序的一个版本位于 /usr/local/bin,而另一个较新的版本位于 /dave/work 目录下。
通过以下 which 命令,我们可以查看 shell 会找到并使用哪个版本的程序:
which rf
Shell 报告说,它找到的是 $PATH 中 /usr/local/bin 目录下的版本。
接下来我们执行以下命令启动该程序:
rf
rf 的 1.0 版本成功运行,这验证了我们的预期,即 shell 找到并执行的是位于 /usr/local/bin 中的版本。
要在当前系统中运行任何其他版本的 rf,我们必须在命令行中指定可执行文件的完整路径,如下所示:
./work/rf
现在,我们明确告诉 shell 我们要运行的 rf 版本的路径,它执行了 1.1 版本。 如果我们更喜欢这个版本,我们可以将它复制到 /usr/local/bin 目录下,以覆盖旧的版本。
假设我们正在开发 rf 的新版本。 在开发和测试过程中,我们需要频繁地运行它,但我们不想将未经发布的开发版本复制到生产环境中。
或者,我们可能下载了一个新版本的 rf,希望在公开发布之前进行一些测试。
如果我们把工作目录添加到 $PATH 中,就可以让 shell 找到我们开发的版本。而且这种更改只会影响到我们,其他用户仍然会使用位于 /usr/local/bin 中的 rf 版本。
将目录添加到 $PATH
您可以使用 export 命令将目录添加到 $PATH。该目录将会被纳入 shell 搜索的文件系统位置列表。当 shell 找到匹配的可执行文件时,它会停止搜索。因此,您需要确保 shell 首先搜索您的目录,然后再搜索 /usr/local/bin。
操作很简单。在我们的示例中,我们输入以下命令将我们的目录添加到 $PATH 的开头,使其成为第一个被搜索的位置:
export PATH=/home/dave/work:$PATH
此命令将 $PATH 的值设置为我们想要添加的目录 /home/dave/work,然后接上整个当前的 $PATH。
第一个 PATH 前面没有美元符号 ($), 因为我们正在设置 PATH 的值。而最后的 $PATH 前面有美元符号,是因为我们引用了存储在 PATH 变量中的内容。另外,请注意新目录和 $PATH 变量名之间的冒号 (:)。
让我们看看修改后的 $PATH 是什么样子:
echo $PATH
我们的 /home/dave/work 目录已被添加到 $PATH 的开头。 冒号将其与 $PATH 的其余部分隔开。
我们键入以下命令来验证 shell 是否首先找到我们版本 rf:
which rf
最终的验证是执行 rf 命令,如下图所示:
rf
shell 找到了 1.1 版本,并从 /home/dave/work 执行了它。
要将我们的目录添加到 $PATH 的末尾,我们只需要将其移动到命令的末尾,如下所示:
export PATH=$PATH:/home/dave/work
使更改永久化
正如贝丝·布鲁克-马西尼亚克所说:“成功没有终点,但成功转瞬即逝。” 一旦您关闭终端窗口,您对 $PATH 所做的任何更改都将消失。要使更改永久生效,您必须将 export 命令添加到配置文件中。
当您将 export 命令添加到 .bashrc 文件中时,每次打开终端窗口都会设置 $PATH。这些被称为“交互式”会话,与需要登录的SSH 会话不同。
在过去,您会将 export 命令放置在 .profile 文件中,以设置登录终端会话的 $PATH。
然而,我们发现,如果我们将 export 命令放在 .bashrc 或 .profile 文件中,它都会正确设置交互式和登录终端会话的 $PATH。您的体验可能会有所不同。为了涵盖所有可能的情况,我们将向您展示如何在两个文件中执行此操作。
在 /home 目录中使用以下命令编辑 .bashrc 文件:
gedit .bashrc
gedit 编辑器打开并加载 .bashrc 文件。
滚动到文件末尾,并添加我们之前使用的以下 export 命令:
export PATH=/home/dave/work:$PATH
保存文件。接下来,要么关闭并重新打开终端窗口,要么使用 dot 命令重新加载 .bashrc 文件,如下所示:
. .bashrc
然后,键入以下 echo 命令检查 $PATH:
echo $PATH
这会将 /home/dave/work 目录添加到 $PATH 的开头。
将命令添加到 .profile 文件的过程相同。 键入以下命令:
gedit .profile
gedit 编辑器启动并加载 .profile 文件。
将 export 命令添加到文件末尾,然后保存。关闭并重新打开新的终端窗口不足以强制重新读取 .profile 文件。要使新设置生效,您必须注销并重新登录,或者使用以下 dot 命令:
. .profile
为所有用户设置 $PATH
要为所有使用系统的用户设置 $PATH,您可以编辑 /etc/profile 文件。
您需要使用 sudo 命令,如下所示:
sudo gedit /etc/profile
当 gedit 编辑器启动时,将 export 命令添加到文件末尾。
保存并关闭文件。这些更改将在其他用户下次登录时生效。
关于安全的注意事项
请确保您没有不小心将前导冒号“:”添加到 $PATH 中,如下所示。
如果这样做,shell 会首先搜索当前目录,这会带来安全风险。假设您下载了一个存档文件,并将其解压到某个目录。您查看文件时,发现另一个压缩文件。您再次调用 unzip 命令来提取该存档。
如果第一个存档中包含一个名为 unzip 的可执行文件,并且它是一个恶意程序,那么您会不小心启动该文件,而不是真正的 unzip 可执行文件。这是因为 shell 会首先搜索当前目录。
因此,当您输入 export 命令时,请务必小心。使用 echo $PATH 来查看当前设置,并确保它是您想要的结果。