我的PATH变量在哪里被设置的?

我正在尝试将我的路径变量设置为这样:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/jgrande/android-sdk-linux/platform-tools:/usr/lib/jvm/java-7-openjdk-amd64/bin/:/usr/lib/jvm/java-7-openjdk-amd64/jre/bin

无论我编辑哪个文件,由于某些原因,变量始终以这种方式结束(其中Java路径重复多次)。
PATH=/usr/local/java/jdk1.7.0_79/bin:/home/jgrande/bin:/usr/local/java/jdk1.7.0_79/bin:/home/jgrande/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/java/jdk1.7.0_79/bin:/usr/local/java/jdk1.7.0_79/jre/bin:/usr/local/java/jdk1.7.0_79/bin:/usr/local/java/jdk1.7.0_79/jre/bin

我已经尝试编辑我的个人.bashrc.profile,还有/etc/environment/etc/bash.bashrc/etc/profile,并在/etc/profile.d中设置。现在,在/etc/profile.d中有一个jdk.sh文件,其中的PATH变量与我上面发布的错误路径变量相同。但是,我尝试注释掉该行甚至完全删除jdk.sh,但问题仍未解决。

另外,我注意到如果我以另一个用户登录,将会有正确的PATH变量,而且不会有重复出现多次的java引起混乱的情况。

所以,我想知道,还有哪些地方可能定义了我的PATH变量,我还没有检查过?


你的 .bash_profile(或 .profile)和/或 .bashrc 是否引用其他文件? - glenn jackman
不,我的.profile和.bashrc都没有引用其他文件。 - user1617942
很奇怪:在/etc/profile.d目录中的文件在每个登录bash会话中都会被加载。所以你的其他用户也应该有这些文件。你为什么不在你的.profile文件中明确设置你的“正确”路径呢? - glenn jackman
是的,我刚刚做了那个,而且成功了。我还是很感兴趣看到是什么覆盖了其他文件(如bash.bashrc、/etc/profile等)。 - user1617942
看起来你在开头有jdk/bin:jgrande/bin,在结尾有jdk/jre/bin,都出现了两次。我不知道"jgrande"是什么,但它看起来很可疑。我会在/etc/profile.d中搜索一下。 - glenn jackman
运行命令 grep -P '^[^#]*\KPATH' /etc/{profile,bash.bashrc,environment} ~/{.bashrc,.profile,.pam_environment} 2>/dev/null 并将输出添加到您的问题中。 - heemayl
3个回答

PATH 可以在 bash 启动时读取的各个文件中设置。要找到它们所有,请运行以下命令:
grep --color -H 'PATH=' ~/.bashrc ~/.profile ~/.bash_profile ~/bash.login \
                     ~/.bash_aliases /etc/bash.bashrc /etc/profile \
                     /etc/profile.d/* /etc/environment 2> /dev/null

那些是标准的文件。如果你还从其中一个文件中获取其他文件,事情可能会变得更加复杂。

无论如何,为了修复它 - 假设你没有对全局文件 /etc/ 做任何操作 - 你可以运行以下命令:

sed -i '/PATH=/d' ~/.bashrc ~/.profile ~/.bash_profile ~/bash.login ~/.bash_aliases 

这将删除那些文件中设置PATH的任何行。现在,在正确的位置添加正确的行:
pp='PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/jgrande/android-sdk-linux/platform-tools:/usr/lib/jvm/java-7-openjdk-amd64/bin/:/usr/lib/jvm/java-7-openjdk-amd64/jre/bin'
[[ -e '~/.bash_profile' ]] && echo "$pp" >> ~/bash_profile ||
                              echo "$pp" >> ~/.profile

当作登录shell调用时,bash会读取.bash_profile(如果存在),如果不存在,则会读取~/.profile(如果存在.bash_login,则会忽略.profile,但我假设它不存在)。因此,上述命令将首先将您所需的路径保存为$pp,并检查是否存在~/.bash_profile。如果存在,则将路径定义添加到其中;如果不存在,则将其添加到~/.profile中。
为了避免这种问题,在将目录添加到$PATH时,请使用函数。将以下行添加到您的~/.profile中,以防止类似问题在未来发生。
pathmunge () 
{ 
    if ! echo "$PATH" | /bin/grep -Eq "(^|:)$1($|:)"; then
        if [ "$2" = "after" ]; then
            PATH="$PATH:$1";
        else
            PATH="$1:$PATH";
        fi;
    fi
}

然后,要在您的$PATH开头添加一些内容,请将以下行添加到~/.profile中:
pathmunge "/path/to/foo"

并将其添加到末尾:
pathmunge "/path/to/foo" after

该函数只会在$PATH中不存在的情况下添加新目录。

1就我所知,我的 Fedora 34 上的(未经编辑的)/etc/profile 文件中有一个 pathmunge 函数,似乎与你分享的那个函数有着相同的目标。 :) - nishanthshanmugham
@nishanthshanmugham 是的,这个函数在基于 Red Hat 的系统中是默认包含的。我最早从一个大约在 2004 年左右运行的 Red Hat 系统上获取了它。 - terdon

要找到所有更改$PATH环境变量的地方,请使用以下方法:
sudo grep -rnw --exclude-dir={boot,dev,lib,media,mnt,proc,root,run,sys,/tmp,tmpfs,var} '/' -e "PATH="

这将搜索您系统上的所有目录,但不包括虚拟文件系统目录。在我的情况下,由于挂载了三个发行版,返回了697个文件。
一个更有针对性的方法是为 OP 搜索特定的路径名。您可以寻找设置包含不寻常部分的 $PATH 的程序。
/usr/local/java/jdk1.7.0_79/bin

在终端中运行此命令:

sudo grep -rnw --exclude-dir={boot,dev,lib,media,mnt,proc,root,run,sys,/tmp,tmpfs,var} '/' -e "/usr/local/java/jdk1.7.0_79/bin"

如果$PATH中的第一个目录不是罪魁祸首,那就搜索最后一个目录:
/usr/lib/jvm/java-7-openjdk-amd64/jre/bin

grep会返回设置$PATH的脚本/文件的名称。


1你真是个救命稻草!有人在xfce4的xinitrc文件中改变了路径...如果没有你的grep命令,我想我可能找不到那个问题! - WLigtenberg

你给你的配置文件应用了一阵乱七八糟的改动!
如果你无法撤销对 /etc 目录下文件的编辑,可能不得不重新安装 bash!
你可以调整个人的 PATH 环境变量而不需要更改 /etc 目录下的文件。例如,参考这些答案。当然,我更倾向于我的答案。
你可以通过阅读 man bash、man less,并关注 man bash 的 INVOCATION 部分,使用 less 查看启动文件来准确定位问题所在。每当它们似乎传递控制权(source, .)时,使用 less 的 ! 命令来查看该文件。使用 less 的 / 命令搜索字符串 PATH= 来查找每个文件中的 PATH 赋值语句。记住要像 bash 一样愚蠢。

你可以使用 diff 命令比较你的 $HOME 和 "另一个用户" 的启动文件(.bashrc.profile.bash_login)。