转自:https://blog.csdn.net/sch0120/article/details/70256318
bash 的 startup 文件
Linux shell 是用户与 Linux 系统进行交互的媒介,而 bash 作为目前 Linux 系统中最常用的 shell,它支持的 startup 文件也并不单一,甚至容易让人感到费解。本文以 CentOS7 系统为例,对 bash 的 startup 文件进行一些必要的梳理和总结。
根据bash手册上的描述:
- /etc/profile: The systemwide initialization file, executed for login shells
- /etc/bash.bash_logout: The systemwide login shell cleanup file, executed when a login shell exits
- ~/.bash_profile: The personal initialization file, executed for login shells
- ~/.bashrc: The individual per-interactive-shell startup file
- ~/.bash_logout: The individual login shell cleanup file, executed when a login shell exits
此外,bash 还支持 ~/.bash_login
和 ~/.profile
文件,作为对其他 shell 的兼容,它们与 ~/.bash_profile
文件的作用是相同的。
备注:Debian系统会使用 ~/.profile
文件取代 ~/.bash_profile
文件,因此在相关细节上,会与 CentOS 略有不同。
“profile”与“rc”系列
通过名字的不同,我们可以直观地将 startup 文件分为“profile”与“rc”两个系列,其实他们的功能都很类似,但是使用的场景不同,这也是大家最容易忽略的地方。
所谓的不同场景,其实就是 shell 的运行模式。我们知道运行中的 bash 有“交互”和“登陆”两种属性,而执行“profile”系列还是“rc”系列,就与shell的这两个属性有关。
原理上讲,“登陆 shell”启动时会加载“profile”系列的 startup 文件,而“交互式非登陆 shell”启动时会加载“rc”系列的 startup 文件。
“profile”系列的执行场景
根据 bash 手册上的描述:
When bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable. The --noprofile option may be used when the shell is started to inhibit this behavior.When a login shell exits, bash reads and executes commands from the files ~/.bash_logout and /etc/bash.bash_logout, if the files exists.
“profile”系列的代表文件为 ~/.bash_profile
,它用于“登录 shell”的环境加载,这个“登录 shell”既可以是“交互式”的,也可以是“非交互式”的。
通过 --noprofile
选项可以阻止系统加载“profile”系列的 startup 文件。
交互式登陆 shell
对于交互式的登陆 shell 而言,CentOS 规定了 startup 文件的加载顺序如下:
登陆过程:
- 读取并执行 /etc/profile 文件;
- 读取并执行 ~/.bash_profile 文件;
- 若文件不存在,则读取并执行 ~/.bash_login 文件;
- 若文件不存在,则读取并执行 ~/.profile 文件;
登出过程:
- 读取并执行 ~/.bash_logout 文件;
- 读取并执行 /etc/bash.bash_logout 文件;
为了完成实验,我新建了一些系统默认没有提供的 startup 文件,例如 /etc/bash.bash_logout。然后在每个文件中打印了文件名,并将它们之间的显式调用语句注释掉,例如 ~/.bash_profile 对 ~/.bashrc 的显式调用。
“交互式登陆shell”的实验结果如下:
1 | [root@localhost ~]# su - chen |
我们看到,因为执行了 ~/.bash_profile
文件,所以优先级更低的 ~/.bash_login
和 ~/.profile
文件并没有被执行。
我们可以删除 ~/.bash_profile
和 ~/.bash_login
文件,这样系统就会找到并执行 ~/.profile
文件:
1 | [root@localhost ~]# mv /home/chen/.bash_profile /home/chen/.bash_profile.bak |
非交互式登陆 shell
对于非交互式的登陆 shell 而言,CentOS 规定了 startup 文件的加载顺序如下:
登陆过程:
- 读取并执行 /etc/profile 文件;
- 读取并执行 ~/.bash_profile 文件;
- 若文件不存在,则读取并执行 ~/.bash_login 文件;
- 若文件不存在,则读取并执行 ~/.profile 文件;
我们注意到,与“交互式登陆 shell”相比,“非交互式登陆 shell”并没有登出的过程,实验也证实了这一点:
1 | -bash-4.2$ bash --login -c "uname -r" |
“rc”系列的执行场景
根据 bash 手册上的描述:
When an interactive shell that is not a login shell is started, bash reads and executes commands from ~/.bashrc, if that file exists. This may be inhibited by using the –norc option. The –rcfile file option will force bash to read and execute commands from file instead of ~/.bashrc.
“rc”系列的代表文件为 ~/.bashrc
,它用于“交互式非登录 shell”的环境加载。
通过 --norc
选项可以阻止系统加载“rc”系列的 startup 文件;通过 --rcfile
选项可以使用指定的文件替代系统默认的 ~/.bashrc
文件。
交互式非登陆 shell
对于交互式的非登陆 shell 而言,CentOS 规定了 startup 文件的加载顺序如下:
- 读取并执行
~/.bashrc
或--rcfile
选项指定的文件
这里需要说明,其实“rc”系列 startup 文件还包括 /etc/bashrc。但是系统并不直接调用这个文件,而是通过 ~/.bashrc
文件显式地调用它。
为了完成实验,我在每个 startup 文件中打印了文件名,并将它们之间的显式调用语句注释掉,例如 ~/.bashrc
对 /etc/bashrc
的显式调用。
“交互式非登陆 shell”的实验结果如下:
1 | [root@localhost ~]# su chen |
startup 文件的默认调用关系
细心的用户会发现,startup 文件的加载并不像上面所述的那样简单。这是因为在 CentOS 中,startup 文件之间还存在着默认的显式调用关系,它们是:
~/.bash_profile
显式调用~/.bashrc
文件;~/.bashrc
显式调用/etc/bashrc
文件;
再看 startup 文件
分别打开 /etc/profile
和 /etc/bashrc
两个文件,我们可以看到:
1 | [root@localhost ~]# head /etc/profile |
由此可见,“profile”系列文件的主要目的在于为“登录 shell”设置环境变量和启动程序;而“rc”系列文件的主要目的在于设置功能和别名。
顺便提一句,Linux 中“rc”是英文“run command”的缩写,表示文件中存放需要执行的命令。其实这也非常符合逻辑,设置功能就要执行 shopt
命令,而设置别名要执行 alias
命令。与“rc”系列互补,“profile”系列用来设置环境变量,它不会去调用这两个命令,但却经常需要使用 export
语句。不信你可以看一看这两个文件。
另外值得一提的是,这两个文件同时提到了一个位置:/etc/profile.d
目录。这个目录用于存放个性化配置脚本,你可以把自己需要的全局配置放入以 .sh 结尾的文件中,系统在执行 /etc/profile
和 /etc/bashrc
文件时,都会择机调用它们。这样做最大的好处是便于维护,而且相对更加安全。
这些文件的编写方法,可以参考目录下已有的文件:
1 | [root@localhost ~]# ls /etc/profile.d/*.sh |
总结
- 对于“登录 shell”而言
- “交互式”执行“登陆”和“登出”相关的“profile”系列 startup 文件
- “非交互式”只执行“登陆”相关的“profile”系列 startup 文件
- 对于“非登陆 shell”而言
- “交互式”执行“rc”系列的 startup 文件
- 而“非交互式”执行的配置文件由环境变量 BASH_ENV 指定。
Linux 中 startup 文件区分全局和个人:全局 startup 文件放在 /etc 目录下,用于设置所有用户共同的配置,除非你清楚地知道你在做的事情,否则不要轻易改动它们;个人 startup 文件放在 ~ 目录下,用于设置某个用户的个性化配置。
~/.bash_profile
会显式调用 ~/.bashrc
文件,而 ~/.bashrc
又会显式调用 /etc/bashrc
文件,这是为了让所有交互式界面看起来一样。无论你是从远程登录(登陆 shell),还是从图形界面打开终端(非登陆 shell),你都拥有相同的提示符,因为环境变量 PS1 在 /etc/bashrc
文件中被统一设置过。
下面我来对startup文件进行一个完整的总结:
startup 文件 | 交互登陆 | 非交互登陆 | 交互非登陆 | 非交互非登陆 |
---|---|---|---|---|
/etc/profile | 直接执行1 | 直接执行1 | - | - |
~/.bash_profile | 直接执行2 | 直接执行2 | - | - |
~/.bash_login | 条件执行2 | 条件执行2 | - | - |
~/.profile | 条件执行2 | 条件执行2 | - | - |
~/.bash_logout | 直接执行3 | 不执行 | - | - |
/etc/bash.bash_logout | 直接执行4 | 不执行 | - | - |
~/.bashrc | 引用执行2.1 | 引用执行2.1 | 直接执行1 | - |
/etc/bashrc | 引用执行2.2 | 引用执行2.2 | 引用执行1.1 | - |
备注:
- “直接执行”表示此文件被系统直接调用,它的执行是无条件的;
- “条件执行”表示此文件被系统调用是有先决条件的(没有优先级更高的文件可用);
- “引用执行”表示此文件不是被系统直接调用的,而是被其他文件显式调用的;
- 后面的数字表示文件被调用的顺序,数字越大调用越靠后;
- “非交互非登陆” shell 的配置文件可以由 BASH_ENV 环境变量指定;
最后我想说的是,知道 startup 文件何时被执行并不是关键,关键是要理解自己的情况应该去修改哪个 startup 文件。
如果你想对 bash 的功能进行设置或者是定义一些别名,推荐你修改 ~/.bashrc 文件,这样无论你以何种方式打开 shell,你的配置都会生效。而如果你要更改一些环境变量,推荐你修改 ~/.bash_profile 文件,因为考虑到 shell 的继承特性,这些更改确实只应该被执行一次(而不是多次)。针对所有用户进行全局设置,推荐你在 /etc/profile.d 目录下添加以 .sh 结尾的文件,而不是去修改全局 startup 文件。