共计 5904 个字符,预计需要花费 15 分钟才能阅读完成。
导读 | 不知道你有没有遇到过这样的场景,当你需要设置一个环境变量,或者运行一个程序设置你的 shell 或桌面环境,但是不知道在哪里是最方便设置的位置。 |
有一些常见的情况,例如从 Debian 的包管理程序到 Iaas 的管理中,很多任务需要设置环境变量才能正常运行。
有时,程序通常只需要在首次登陆时运行一次,例如 xrandr 命令。
此外,有的程序偶尔会被注入到 shell 中,例如 rbenv,rvn 或 SitePoint’s 自己的 envswith 程序。
让我们来看看在 Debian GNU/Linux Jessie 安装中出现的一些常见选项,并尝试理解这一切。
默认情况下,Debian 提供 /etc/profile 文件,这个文件用来设置 $PATH 变量($PATH 通常用来声明命令的搜索路径),可以立即生效。下面的代码是 /etc/profile 的一部分。
if ["`id -u`" -eq 0]; then
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
else
PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"
fi
export PATH
为了方便,root 用户(ID 为 0)和其他任何用户的路径都不同。这是因为系统二进制目录(sbin 目录)位置传统上是作为系统管理程序、或必须以 root 身份运行的程序存放的保留位置。而 games 路径对于 root 用户来说是省略的,因为不到非必要的时候,绝不可能使用 root 用户来运行游戏程序。
接下来,/etc/profile 处理 $PS1 变量的设置,$PS1 变量是用来设置主提示字符串(即用户登陆时显示的字符)。除了系统的 shell 是 Bash 以外,系统 $PS1 变量默认设置的是 $ (root 用户默认是 #)。如果系统的 shell 使用的是 Bash,则 /etc/bash.bashrc 文件会替代 $PS 变量来处理主提示字符串(特殊情况除外)。后面我们会简短地说一下 /etc/bash.bashrc。
所以从这一点上,我们可以推断 /etc/profile 在登陆期间(例如使用 login 命令)会被所有的 shell 读取。/etc/profile 调用 id 命令来读取用户 ID,而不是使用更高效的 Bash 内置变量 ${UID}。Bash 使用特定来源的配置,而不是定义一个花哨的 shell 提示符,因为 Bash 支持反斜杠转义的特殊字符,例如 \u(用户名) 和 \h (主机名),许多其他的 shell 都不支持这样定义。/etc/profile 应该尝试和 POSIX 兼容,以便与用户可能自己安装的任何 shell 兼容。
Debian GNU/linux 通常预装 Dash,Dash 是一个仅仅旨在实现 POSIX(和一些伯克利)扩展的基本 shell。如果我们修改 /etc/profile(修改之前先备份)让 PS1=’$‘这一行设置不同的值,然后模拟一个 Dash 登录(通过 dash - l 命令),我们可以看到 Dash 会使用我们自定义的提示。但是,如果我们调用不带 - l 参数的 dash 命令,dash 将不会读取 /etc/profile。此时 Dash 会使用默认值(这意味着此时 PS1 的值是我们修改之前的值)。
最后一点和 /etc/profile 相关的趣事是下面的代码片段:
if [-d /etc/profile.d]; then
for i in /etc/profile.d/*.sh; do
if [-r $i]; then
. $i
fi
done
unset i
fi
换句话说,任何匹配 /etc/profile.d/*.sh 的可读内容都会被当作变量来源。这个非常重要,因为它表明直接编辑 /etc/profile 从来都不是实际需要的(所以恢复你之前的备份)。上面定义的任何变量都可以通过在一个单独的文件中配置,然后覆盖 /etc/profile 中的设置。这样做的好处是:它允许系统升级时自动添加相应的变更到 /etc/profile 文件中。因为 Debian 的 Apt 包管理系统通常不会修改默认的配置文件。
/etc/profile 存在的一个潜在问题是,它位于系统范围的路径中。这意味着修改它会影响这个系统上的所有用户。在个人计算机上,这可能不是太大的问题,但是修改它同时还需要 root 权限。由于这些原因,每个单独的 Bash 用户账户可以创建~/.bash_profile, ~/.bash_login 和 ~/.profil 这几个文件中的任意一个作为 Bash 的配置文件来源。在列出的顺序中第一个被找到的文件会被作为配置文件,其余的都会被忽略。
其他的 shell,例如 Dash,支持相似的东西,但是只会查找~/.profile 文件。这允许用户为 Bash 特定的应用场景配置单独的.bash_profile 文件,如果在某些时候需要切换到 Dash 或其他 shell 作为登录 shell(例如通过 chsh -s dash 命令)。可以保留~/.profile 作为这些 shell 的配置文件。
需要牢记的一点是,默认的 Debian 框架目录(/etc/skel,用于存放要复制到新用户账户主目录的文件和目录)包含.profile 文件,但不包含.bash_profile 和.bash_login 文件。此外 Debian 使用 Bash 作为默认的 shell,因此,许多 Debian 用户习惯于将他们的 Bash 登录 shell 设置放在.profile 文件中。
我曾经看到过一些项目的安装说明,例如 RVN,这个项目建议用户创建一个.bash_profile 文件,但是这样做是非常危险的,根据上面提到的知识我们知道,这个会改变用户的 shell 环境。即使用户没有修改.profile 文件,它也可能利用默认~/.profile 功能,将~/bin 添加到 $PATH 环境变量。一个可能提高安全性的选项是,在创建用户的账户之前,将.bash_profile 作为.bash_rc 的符号链接文件,放到 /etc/skel 目录中。
如果我们查看 Debian Jessie 的默认.profile 脚本,我们可以看到下面的代码片段:
# if running bash
if [-n "$BASH_VERSION"]; then
# include .bashrc if it exists
if [-f "$HOME/.bashrc"]; then
. "$HOME/.bashrc"
fi
fi
这和我们在 /etc/profile 里面看到的相似,如果 shell 是 Bash,且发现了 /etc/bash.bashrc 文件,/etc/bash.bashrc 文件就被当作 Bash 的配置文件。这一点的意义将在下一节讨论。
启动的时候,Bash 会同时读取 /etc/bash.bashrc 和~/.bashrc,但是只有在 Bash Shell 作为交互式 Shell 而不是登录 Shell 启动时(意味着通过 xtem 启动),会依照这种顺序,这是 Bash Shell 的标准行为。然而,Debian 分别从 /etc/profile 和~/.profile 登录脚本中获取配置文件。这会显著地改变行为,使得 /etc/bash.bashrc 和.bashrc(如果它们存在)总是在 Bash 启动时调用,而不管是不是登录 Shell。不要期待这种情况在不同地发行版中是一样的。
.bashrc 是一个添加命令别名的好地方,实际上,一些用户拥有太多的别名,以至于他们宁愿将别名都放在一个单独的文件中去。Debian 的默认.bashrc 会查找.bash_alias,如果这个文件存在的话,会将它作为别名配置来源。所以你可以在这个文件中随意保存所有的 Bash 别名。如果用户愿意的话,.bashrc 文件也是用户重写 shell 变量,例如 $PS1 或者 $HISTSIZE 的绝佳位置。Debian 的默认.bashrc 有超过 100 行,但是仍然可以非常清晰地阅读,且有良好地注释。见名知意,.bashrc 不是其他非 Bash shell 的配置文件来源。
如果你是一个 GNU/Linux 桌面用户,通过显示管理器本地登录(而不是通过 getty 登录程序),则 /etc/profile 和~/.profile 不会像预期的那样工作。一些显示管理器会直接将这些文件视为错误地配置文件,例如 Gnome 显示管理器。但一些其他的显示管理器,例如 LightDm 不会这样。幸运的是,你还有一些其他的选项。
当启动 X Window 系统会话时(不管是用显示管理或从虚拟终端启动 startx),将会执行 /etc/X11/Xsessionshell 脚本。这基本上相当于登录 shell 调用 /etc/profile。这个只对 X Window 生效,并且不是将其作为源配置文件,而是直接执行。但是它也相当复杂,类似于 /etc/profile 怎么从 /etc/profile.d 目录中的脚本读取配置,怎么从 /etc/X11/Xsession.d/ 目录下的 /etc/X11/Xsessions 脚本中读取配置。在 /etc/X11/Xsession.d 目录下的所有脚本名称都以数字开头,因此所有的脚本都会按照数字顺序来读取。
Debian Jessie 包含一个名叫 40×11-common_xsessionrc 的文件,这个文件做的工作就是检查~/.xsessionrc 是不是可读的,如果是就用它作为配置文件的来源。这就使得~/.xsessions 是一个加载环境变量或者运行一个一次性使用程序(例如 xrandr 或 xmodmap)的完美位置(仅适用于 X 会话)。如果你希望的话,你同样可以将 /etc/profile 或~/.profile 作为来源。那么任何指定的环境变量也都会被你的会话管理器继承(如果还没有继承的话)。请注意,默认情况下.xsessionrc 是不存在的,需要你自己创建这个文件。
如果我们继续浏览 /etc/X11/Xsession 中的文件,我们会发现 50×11-common_determine-startup 会决定加载哪个会话管理器。如果~/.xsessions 文件存在而且是可执行的,它会被保存并且随后作为 99×11-common_start 的一部分执行,当~/.xsession 用于运行会话管理器,X 会话将会被注销。并且当这个脚本终止时,你会返回到显示管理器登录界面。
和~/.xsessionrc 相似,~/.xsession 默认也是不存在的,在你需要的时候你可以创建一个。你可能会创建一个类似下面给的简单的.xsession 脚本
# Start our session manager of choice.
#
exec x-session-manager
其中 x -session-manager 默认设置为通过 update-alternatives 命令配置的任何内容,这样,你可以轻松地更改系统范围默认地会话管理器,只需要将 x -session-manager 替换为 /usr/bin/startfce4(切换到 XFCE),其他的用户账户将完全不受影响。
当然,许多显示管理器提供从登录界面直接选择公共会话管理器的能力,所以这个文件通常是不必要的。然而.xsession 提供了更多地灵活性,你可以用任何程序调用这个文件,而不仅仅是会话管理器。例如,在这里你可以在 while 循环中调用 chromium 或者 iceweasel,而不是执行基本的 kiosk 模式设置。
我们前面介绍了当用户运行交互式 Bash 登录 shell 时读取的文件,但是如果你想在注销以后仍然运行程序该怎么办?对于这个用例,~/.bash_logout 文件就非常方便了。在 Debian 中默认的配置仅用于清除屏幕(我认为从安全角度来说很重要),但是可以轻微地想象以下就知道能用于其他目的,例如,在你离开你的机器之前显示一个几秒钟的提醒。
主要的限制因素在于.bash_logout 仅在注销交互式 shell 时读取,并且并不能假定它在注销 X 会话时会被加载。
上面那些已经为你介绍了大部分的通用选项。其他的选项可能会存在,取决于你的安装环境(例如 /etc/environment),但是我不认为他们可能在其他的平台上存在,并且极少有需要去接触它们。
那么你应该在哪放置你的系统范围环境变量?如果你希望一个环境变量可以影响所有用户,/etc/profiled./someifle.sh 会是一个好的选择。但是,这假设你是使用一个登录管理器以 /etc/profile 作为配置来源。如果不是这样,你可以(作为一个管理员)添加一个脚本到 /etc/X11/Xsession.d/ 来替代 /etc/profile 作为配置来源。
如果你希望一个脚本可以找到一个私人目录路径,并且添加它到你的 PATH 中,你需要考虑这个目录是不是会移动很多东西,如果你向.profile 添加代码来实现,用户需要注销然后再登录来更改用户会话期间的 PATH。如果你将代码添加到.bashrc 中,这意味着代码将在用户每次打开 xterm 时执行,如果执行大约半秒以上可能就不太理想。所以这是一个权衡取舍的问题。
如果你仅仅是为了你个人登录会话时的一个环境变量,且它只关心 X 会话,你可以将它添加到~/.xsessionrc 中。这样做的优点是,它通常将可用于通过 X 会话管理器启动的所有程序,因为它在启动 X 会话管理器之前被设置,并且被继承。例如,某些图形驱动程序可以通过运行
export vblank_mode=0
来禁用 vsync。所以位于.xsessionrc 中的变量会影响到所有的程序。
然而如果这一行被添加到.bashrc 中,则只有通过 xterm 登录的程序会被影响。通过一个窗口管理器启动的程序照常运行。你可以把它添加到.profile,并且从.xessionrc 作为.profile 的来源。但是之后,当你的 X 服务没有在运行的时候,你就不需要导出环境变量。
希望你现在可以更好地了解了登录和注销脚本在 Debian GNU/Linux 系统上的工作原理。如果你已经为这些登录和注销脚本创建、或者遇到任何特别有趣或有创新的用途,请在评论中告诉我们你是如何做到的。
在接下来的系列中,我们将讨论 dotfile 管理选项。