跳转到内容

4. bash 启动脚本

启动脚本是 bash 启动时自动执行的脚本。用户可以把一些环境变量的设置和 aliasumask 设置放在启动脚本中,这样每次启动 Shell 时这些设置都自动生效。思考一下, bash 在执行启动脚本时是以 fork 子 Shell 方式执行的还是以 source 方式执行的?

启动 bash 的方法不同,执行启动脚本的步骤也不相同,具体可分为以下几种情况。

4.1. 作为交互登录 Shell 启动,或者使用 --login 参数启动

交互 Shell 是指用户在提示符下输命令的 Shell 而非执行脚本的 Shell,登录 Shell 就是在输入用户名和密码登录后得到的 Shell,比如从字符终端登录或者用 telnet / ssh 从远程登录,但是从图形界面的窗口管理器登录之后会显示桌面而不会产生登录 Shell(也不会执行启动脚本),在图形界面下打开终端窗口得到的 Shell 也不是登录 Shell。

这样启动 bash 会自动执行以下脚本:

  1. 首先执行 /etc/profile ,系统中每个用户登录时都要执行这个脚本,如果系统管理员希望某个设置对所有用户都生效,可以写在这个脚本里
  2. 然后依次查找当前用户主目录的 ~/.bash_profile~/.bash_login~/.profile 三个文件,找到第一个存在并且可读的文件来执行,如果希望某个设置只对当前用户生效,可以写在这个脚本里,由于这个脚本在 /etc/profile 之后执行, /etc/profile 设置的一些环境变量的值在这个脚本中可以修改,也就是说,当前用户的设置可以覆盖(Override)系统中全局的设置。 ~/.profile 这个启动脚本是 sh 规定的, bash 规定首先查找以 ~/.bash_ 开头的启动脚本,如果没有则执行 ~/.profile ,是为了和 sh 保持一致。
  3. 顺便一提,在退出登录时会执行 ~/.bash_logout 脚本(如果它存在的话)。

4.2. 以交互非登录 Shell 启动

比如在图形界面下开一个终端窗口,或者在登录 Shell 提示符下再输入 bash 命令,就得到一个交互非登录的 Shell,这种 Shell 在启动时自动执行 ~/.bashrc 脚本。

为了使登录 Shell 也能自动执行 ~/.bashrc ,通常在 ~/.bash_profile 中调用 ~/.bashrc

shell
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

这几行的意思是如果 ~/.bashrc 文件存在则 source 它。多数 Linux 发行版在创建帐户时会自动创建 ~/.bash_profile~/.bashrc 脚本, ~/.bash_profile 中通常都有上面这几行。所以,如果要在启动脚本中做某些设置,使它在图形终端窗口和字符终端的 Shell 中都起作用,最好就是在 ~/.bashrc 中设置。

下面做一个实验,在 ~/.bashrc 文件末尾添加一行(如果这个文件不存在就创建它):

shell
export PATH=$PATH:/home/akaedu

然后关掉终端窗口重新打开,或者从字符终端 logout 之后重新登录,现在主目录下的程序应该可以直接输程序名运行而不必输入路径了,例如:

bash
~$ a.out

就可以了,而不必

bash
~$ ./a.out

为什么登录 Shell 和非登录 Shell 的启动脚本要区分开呢?最初的设计是这样考虑的,如果从字符终端或者远程登录,那么登录 Shell 是该用户的所有其它进程的父进程,也是其它子 Shell 的父进程,所以环境变量在登录 Shell 的启动脚本里设置一次就可以自动带到其它非登录 Shell 里,而 Shell 的本地变量、函数、 alias 等设置没有办法带到子 Shell 里,需要每次启动非登录 Shell 时设置一遍,所以就需要有非登录 Shell 的启动脚本,所以一般来说在 ~/.bash_profile 里设置环境变量,在 ~/.bashrc 里设置本地变量、函数、 alias 等。如果你的 Linux 带有图形系统则不能这样设置,由于从图形界面的窗口管理器登录并不会产生登录 Shell,所以环境变量也应该在 ~/.bashrc 里设置。

4.3. 非交互启动

为执行脚本而 fork 出来的子 Shell 是非交互 Shell,启动时执行的脚本文件由环境变量 BASH_ENV 定义,相当于自动执行以下命令:

shell
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi

如果环境变量 BASH_ENV 的值不是空字符串,则把它的值当作启动脚本的文件名, source 这个脚本。

4.4. 以 sh 命令启动

如果以 sh 命令启动 bashbash 将模拟 sh 的行为,以 ~/.bash_ 开头的那些启动脚本就不认了。所以,如果作为交互登录 Shell 启动,或者使用 --login 参数启动,则依次执行以下脚本:

  1. /etc/profile
  2. ~/.profile

如果作为交互 Shell 启动,相当于自动执行以下命令:

shell
if [ -n "$ENV" ]; then . "$ENV"; fi

如果作为非交互 Shell 启动,则不执行任何启动脚本。通常我们写的 Shell 脚本都以 #! /bin/sh 开头,都属于这种方式。