比较烦人的 bash 初始化脚本 ~/.profile 失效问题
除了系统级的初始化脚本以外,通常有两个用户级的脚本:.bashrc, .profile。其中,前者是在每一次运行 bash 时导入,而后者是以 bash 作为登陆 shell 登陆进系统的时候起效。
很久以前,我图省事都是把配置脚本写在 .bashrc 中的,没有发现有什么问题。后面却发现写在 .profile 中的配置是不生效的。当我的配置写得越来越多,试图重新整理时发现了此问题。
原因有些复杂。UNIX shell 太多了,不同的 shell 有不同的初始化脚本。 .bashrc .bash_profile .bash_login .bash_logout
这几个文件是 bash 所拥有的,如果换成其它的 shell,是不会鸟它们的。 .profile 有些特殊,它大概不属于任何 shell,而是一个用户级的“全局”配置脚本。也就是说其它 shell 也承认这个文件,会在登陆时读取它。但是它的优先级是最低的。当home下存在 .bash_profile 或者 .bash_login 这两个文件的其中之一的时候,bash 就不鸟它了。那么,为什么我们的目录底下并没有 .bash_profile 或者是 .bash_login,但是 .profile 仍然不生效呢?因为,它是 login shell 的初始化脚本,然而 X 并不是 shell ...... 所以 X 仍然不鸟它。
有几个解决方案:
-
把配置写在 .bashrc 中。然后在 .profile 中 source .bashrc(在 Debian 中,默认的 .bashrc 中已经有这个动作了)。在桌面中打开终端窗口,.bashrc 肯定是会读取的。当从字符终端(ssh)登陆时,.profile 会被读取,然后接着又 source .bashrc。配置仍然生效。
-
X 拥有自己的初始化脚本 ~/.xsessionrc ,可以在里面加一句:
if [ -r ~/.profile ]; then . ~/.profile; fi
这是Debian Wiki 上的解决办法。但是这一方案有一个潜在的问题,那就是它的语法受限。原因大概是,X session 是由 /bin/sh 启动的,而非 bash,所以它支持的语法有限。我曾经试过将需要添加进 $PATH 的目录写在一个数组中,然后 for 循环遍历逐个添加进环境变量中。结果死活不生效,然而自己开一个终端窗口,手动 source 该文件的确是生效的。这个问题排查了很久才弄清楚原因。
- 在启动 X 会话前,先启动一个 bash 登陆 shell,由它 source .profile,然后再启动 X session。编辑 ~/.xsession 为如下内容:
#!/bin/bash --login
exec x-session-manager
要注意的是,这是一个可执行脚本,记得加上可执行权限 chmod +x ~/.xsession
。同时,删除掉上面的 .xsessionrc,不需要了。问题总算圆满地解决。