启动 bash 终端后,我注意到 PATH 变量包含重复的条目。我的终端启动了登录外壳,so~/.bash_profile
是来源,后面是~/.profile
和~/.bashrc
。只有在~/.profile
我才创建重复的路径条目。
迂腐地说,这是应该获取的文件的获取顺序:
Sourced /etc/profile
Sourced /etc/bash.bashrc
Sourced .bash_profile
Sourced .profile
Sourced .bashrc
在有人将其标记为“PATH 变量包含重复项”的重复项之前,请继续阅读。
起初,我认为这与~/.profile
获取两次有关,因此每当获取文件时我都会将文件写入日志文件,令人惊讶的是它只记录了一个条目,这告诉我它只获取了一次。更令人惊讶的是,当我注释掉 中的条目时~/.profile
,这些条目仍然出现在PATH
变量中。这使我得出三个结论,其中之一很快就被排除了:
- Bash 忽略有效的 bash 注释并仍然执行注释的代码
- 有一个脚本可以读取
~/.profile
并忽略任何打印输出的代码(例如日志文件) - 我的另一份副本是
~/.profile
从其他地方获取的
第一个,由于一些快速测试,我很快得出结论并非如此。第二个和第三个选项是我需要帮助的地方。
如何收集终端启动时执行的脚本列表?我echo
在检查的文件中使用了这些文件,以了解它们是否来自 bash,但我需要找到一个结论性的方法,该方法可以在终端准备好开始输入内容时跟踪执行情况。
如果上述不可能,那么任何人都可以建议我还可以在哪里查看正在运行的脚本。
以后的参考
这是我现在用于添加到我的路径的脚本:
function add_to_path() {
for path in ${2//:/ }; do
if ! [[ "${!1}" =~ "${path%/}" ]]; then # ignore last /
new_path="$path:${!1#:}"
export "$1"="${new_path%:}" # remove trailing :
fi
done
}
我这样使用它:
add_to_path 'PATH' "/some/path/bin"
该脚本在添加路径之前检查变量中是否已存在该路径。
对于 zsh 用户,您可以使用等效的方法:
# prepends the given path(s) to the supplied PATH variable
# ex. add_to_path 'PATH' "$(go env GOPATH)/bin"
function add_to_path() {
# (P)1 path is expanded from $1
# ##: Removes leading :
local -x pth="${(P)1##:}"
# (s.:.) splits the given variable at :
for p in ${(s.:.)2}; do
# %%/ Remove trailing /
# :P Behaves similar to realpath(3)
local p="${${p%%/}:P}"
if [[ ! "$pth" =~ "$p" ]]; then
pth="$p:$pth"
fi
done
export "$1"="${pth%%:}"
}
编辑 28/8/2018
我发现我可以用这个脚本做的另一件事是修复路径。所以在我的文件的开头.bashrc
,我做了这样的事情:
_temp_path="$PATH"
PATH='/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin'
add_to_path 'PATH' "$_temp_path"
unset _temp_path
这取决于你应该PATH
从什么开始。先检查PATH
一下再决定。
答案1
如果您的系统有,strace
那么您可以列出 shell 打开的文件,例如使用
echo exit | strace bash -li |& grep '^open'
(-li
表示交互式登录 shell;仅用于-i
交互式非登录 shell。)
这将显示 shell 打开或尝试打开的文件列表。在我的系统上,它们如下:
/etc/profile
/etc/profile.d/*
(中的各种脚本/etc/profile.d/
)/home/<username>/.bash_profile
(这失败了,我没有这样的文件)/home/<username>/.bash_login
(这失败了,我没有这样的文件)/home/<username>/.profile
/home/<username>/.bashrc
/home/<username>/.bash_history
(命令行的历史;这不是脚本)/usr/share/bash-completion/bash_completion
/etc/bash_completion.d/*
(提供自动完成功能的各种脚本)/etc/inputrc
(定义键绑定;这不是脚本)
用于man strace
获取更多信息。
答案2
重新提出这个问题是因为strace
这里太过分了。
执行 bash 并将其从输出中提取出来。-li
是交互式登录,-x
打印出 bash 内部正在做什么,并-c exit
告诉 bash 立即终止。用于sed
过滤掉source
命令或.
别名。
/bin/bash -lixc exit 2>&1 | sed -n 's/^+* \(source\|\.\) //p'
答案3
请考虑这个关于为什么 ~/.profile 被加载两次的解释。
在大多数运行 GUI 的 Linux 系统上,~/.profile 文件在您通过 GUI 登录时就已被处理。当您随后打开终端窗口时,通常不应将 shell 作为登录 shell 启动,除非您有非常具体的用例。默认情况下,Linux 发行版上的大多数 GUI 都正确实现了此行为。
作为参考,您可以阅读答案的第二段https://unix.stackexchange.com/a/119675/521859对此进行了解释。
如果您确实从 GUI 启动终端并指定 shell 需要使用登录选项,那么很可能会再次加载 ~/.profile。
您可以通过TTY或SSH登录进行比较来验证是否是这种情况,然后检查您的重复配置问题是否得到解决。或者您可以在 GUI 的终端程序中验证/禁用 shell 登录选项。
问候,杰弗里