@Shell 的两种启动方式以及环境变量的配置
本文内容:login shell 与 non-login Shell,interactive Shell 与 non-interactive shell;linux 的 shell 启动脚本(e.g. /etc/profile, /etc/bashrc, ~/.bash_profile, ~/.bashrc, etc.)文件及其加载顺序
shell 是什么?
Shell 是指操作系统中,提供访问内核服务的程序,与之相对的是操作系统的内核(linux kernel)。--维基百科
我们平常使用的命令行界面的 Shell,只是众多的 Shell 的一种。通常,根据使用方式的不同 Shell 可以分成两类:命令行界面(CLI)和图形界面(GUI)。本文介绍的是命令行界面(CLI)。
命令行界面 Shell(以下简称 CLI-Shell),也是有很多种,通过运行cat /etc/shells
命令,可以查看本机可以使用哪些 Shell。以下是 macOS 下可以使用的Shell:
# List of acceptable shells for chpass(1).
/bin/bash
/bin/csh
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh
在 Linux 发行版中,默认的 Shell 就是 Bash,接下来的内容都会以 Bash 为例来进行说明。
在日常的工作中,经常会接触到环境变量、命令别名等概念。这些东西如何配置?如何生效?
这就涉及到 Shell 的启动脚本了,在介绍启动脚本前,先一起来认识下 Shell 的不同启动方式。
Shell 不同的启动方式
Shell 有几种不同的运行模式,login shell与non-login shell,interactive shell与non-interactive shell(比如执行shell脚本)。
交互式 shell 和 非交互式 Shell ( interactive shell and non-interactive shell )
- 交互式模式: 就是在终端上执行,shell等待你的输入,并且立即执行你提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、退出。当你退出后,shell也终止了。
- 非交互式模式: 以shell script(非交互)方式执行。在这种模式 下,shell不与你进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾EOF,shell也就终止了。
可以通过打印$-
变量的值(代表当前 Shell 的选项标志),查看其中的i
选项(表示 interactive shell)来区分交互式与非交互式 shell
zodas@localhost:~$ echo $-
himBH #这是返回的内容
zodas@localhost:~$ ./tesh.sh echo $-
hb #这是返回的内容
登录 Shell 和非登录 Shell ( login shell and non-login shell )
- 登录shell: 是需要用户名、密码登录后才能进入的shell(或者通过”–login”选项生成的shell)。
- 非登录shell: 不需要输入用户名和密码即可打开的Shell,例如:直接命令“bash”就是打开一个新的非登录shell,在Gnome或KDE中(带图形界面的 linux 中)打开一个“终端”(terminal)窗口程序也是一个非登录shell。
执行exit
命令,退出一个shell(登录或非登录shell);
执行logout
命令,退出登录shell(不能 退出非登录shell)。
zodas@localhost:~$ bash --login
zodas@localhost:~$ logout
zodas@localhost:~$ bash --login
zodas@localhost:~$ exit
logout
zodas@localhost:~$ bash
zodas@localhost:~$ logout
bash: logout: not login shell: use ‘exit’
zodas@localhost:~$ exit
exit
那么,如何判断一个已经打开的 Shell 是 login shell 还是 non-login shell?
A login shell is one whose first character of argument zero is ‘-’, or one invoked with the --login option. from bash reference manual
bash 是 login shell 时,其进程名为”-bash“ 而不是”bash”。 比如下面的命令行演示:
# 在 login shell 中:
[zodas@localhost ~]$ echo $0
-bash
[zodas@localhost ~]$ ps -ef | grep '\-bash' | grep -v grep
root 16823 16821 0 May06 pts/0 00:00:00 -bash
zodas 21135 21134 0 May07 pts/1 00:00:00 -bash
#在一个非登陆shell中:
zodas@localhost:~$ echo $0
/bin/bash
zodas@localhost:~$ ps -ef | grep '\-bash' | grep -v grep
zodas@localhost:~$
Shell 启动脚本(配置文件)
以 Bash 为例,Shell 的启动脚本有以下:
作用 | |
---|---|
/etc/profile | 为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行。当此文件被修改,需要重启系统,才能 永久 生效。当你切换用户时,该文件会执行一次;通过source /etc/profile 的方式,只能在当前终端生效,当重新打开一个终端时,这个新的终端时不会加载修改后的/etc/profile 的,即新的终端人就是使用旧的环境变量 |
~/.bash_profile | bash 个人设置,只会对当前的登录用户生效 |
~/.bash_login | 登录 Bash 会执行,也只会对当前登录的用户生效 |
~/.profile | 通用的个人设置,仅对当前登录用户生效。 |
~/.bash_logout | 退出登录时执行,也只会对当前登录的用户生效 |
------------------ | -------------------------------------------------------------------------------------- |
/etc/bashrc | 为每一个运行 bash shell 的用户执行该文件 |
/etc/bash.bashrc | 同上(Ubuntu 中没有 /etc/bashrc 文件,与之对应的是 bash.bashrc 文件) |
~/.bashrc | 该文件包含专用于某个用户的 bash shell 信息,当该用户登录时以及每次打开新的 shell 时,该文件被读取。/etc/profile 与 ~/.bashrc 是”父子“关系,后者继承前者 |
~/.bashrc 与 ~/.bash_profile 的区别:前者针对 non-login shell,后者针对 login shell;
~/.bashrc 与 /etc/bashrc 的区别:前者是用户 bash shell 配置,后者是全局 bash shell 配置
tips:系统优先加载/etc
目录下的全局配置文件,再加载~
用户目录下的文件,全局配置文件需要重启系统才能永久生效,用户目录下的配置文件重新登录后即可生效
Shell 启动脚本的读取顺序
login shell 与 non-login shell 的主要区别在于它们启动时会读取不同的配置文件,从而导致 Shell 环境的不同。
- login shell 启动时配置文件的读取顺序
1. 首先执行 /etc/profile
2. 然后依次查找 ~/.bash_profile, ~/.bash_login, ~/.profile 三个文件,找到第一个存在且可读的文件来执行。
3. 如果存在 ~/.bash_logout,退出时会执行这个脚本。
这三个文件属于用户个人偏好设置。
另外,用户配置文件是可以覆盖 /etc/profile 中的全局设置的。
- non-login shell 启动时配置文件的读取顺序
1. 先执行 /etc/bash.bashrc
2. 后执行 ~/.bashrc
实例1:启动系统,以图形模式登录时(即以login shell方式)
1. 首先执行 /etc/profile
2. 然后依次查找 ~/.bash_profile, ~/.bash_login, ~/.profile 三个文件,找到第一个存在且可读的文件来执行。
3. 如果存在 ~/.bash_logout,退出时会执行这个脚本。
这三个文件属于用户个人偏好设置。
另外,用户配置文件是可以覆盖 /etc/profile 中的全局设置的。
实例2:图形模式登录后,打开终端时(此时是以 non-login shell 方式)
1. 先执行 /etc/bash.bashrc
2. 后执行 ~/.bashrc
实例3:深度理解配置文件的加载顺序
通过下面这个例子,进一步理解配置文件的加载顺序:
step1:启动 Ubuntu 系统,在终端1中修改 /etc/profile 文件增加 java 环境变量,但不重启系统。
step2:在终端1中,不执行 source /etc/profile,然后输入 java -version ,返回 Command not found
step3:在终端1 中,执行 source /etc/profile,然后输入 java -version ,发现java 配置成功
step4:打开一个新的终端,即终端2,输入 java -version,返回 Command not found
step5:在终端2中,执行 sudo su -l ,即进入到 login shell 模式(或者说是切换到一个新的用户),输入 java -version ,发现java 配置成功
step6:再打开一个新的终端,即终端3,然后输入 java -version,发现又返回 Command not found
step7:重启系统,打开一个终端,输入 java -version,发现java 配置成功
step8:再打开一个终端,输入 java -version,发现java 配置成功
从上面这个例子中我们可以得到以下结论:
- /etc/profile 修改后,需要重启系统后才会对系统下的所有用户永久生效
- /etc/profile 修改后,若不重启,也可以通过
source /etc/profile
或在当前终端下切换用户的方式,使其在当前终端立即生效,但打开一个新的终端后,又会失效 - 在图形界面下,每打开一个新的终端,其实际上是 non-login 方式,即其默认的加载顺序是
/etc/bashrc --> ~/.bashrc
总结
在知晓各个启动脚本的特性和用途后,我们可以对机器上的启动配置做如下优化:
- 将每个用户都需要使用的通用配置加入
/etc/profile
- 将个人的、不涉及到终端特性的通用配置加入
~./profile
- 将个人的、Bash 相关的配置加入
~/.bash_profile
- 将通用的 Bash 配置加入
/etc/bashrc
参考
- Bash Reference Manual
- Shell 启动详解和配置实践
- 理解 bashrc 和 profile
- Linux 安装软件时,/etc/profile,/etc/bash.bashrc, ~/.profile, ~/.bashrc 的用途
- linux 下
su
与su -
命令的本质区别 - su ,su - ,sudo区别--pianzif
- login shell 与 non-login shell 的区别
- linux 下的 source 命令
- /etc/bashrc,用户目录下.bashrc有什么区别?--会编程的卡卡西
- 交互式 Shell 和非交互式 Shell,登录 Shell 和非登录 Shell 的区别--笑遍世界