注:实验环境为 - CentOS 7 - Bash
Login & Non-Login
1 2 3 4 5 6 Connecting to 10.10.8.137:22... Connection established. To escape to local shell, press Ctrl+Alt+]. Last login: Wed Oct 18 21:55:30 2023 from 10.10.8.1 [root@localhost ~]#
可以看到有一个 Last login
的时间和 IP
。
这时,有两种命令可以断开 SSH
连接:
logout
:在用户登录 Shell
会话时,用于安全地注销用户并终止该会话。
exit
:退出当前 Shell
环境,并返回到上一级 Shell
或关闭终端会话。
1 2 3 4 5 6 7 8 [root@localhost ~]# logout Connection closed. Disconnected from remote host(CentOS 7) at 00:28:31. [root@localhost ~]# exit logout Connection closed. Disconnected from remote host(CentOS 7) at 00:28:49.
可以看到两者的具体作用上有细微差别,比如在使用 bash 命令开启一个子 Shell
时,却不能使用 logout
退出,只能使用 exit
:
1 2 3 4 5 6 7 8 [root@localhost ~]# bash [root@localhost ~]# ps -f UID PID PPID C STIME TTY TIME CMD root 52446 52442 0 15:52 pts/1 00:00:00 -bash root 52486 52446 0 15:52 pts/1 00:00:00 bash root 52515 52486 0 15:52 pts/1 00:00:00 ps -f [root@localhost ~]# logout bash: logout: not login shell: use `exit'
这是为什么?很简单,因为 bash
的子 Shell
是一个 Non-login
,对于一个非登录 Shell
来说,这个 Shell
没有用户登录又何来用户登出呢。
Login Shell
在 sudo
命令中有个参数 -i
,用于运行一个 Login Shell
:
1 2 3 4 5 [root@localhost ~]# sudo --help | grep 'login' -i, --login run login shell as the target user; a command may also be specified [root@localhost ~]# sudo -i echo $0 -bash
1 2 3 4 5 6 7 [root@localhost ~]# su --help | grep 'login' -, -l, --login make the shell a login shell [root@localhost ~]# su - centos7 Last login: Thu Oct 19 00:37:52 CST 2023 on pts/2 [centos7@localhost ~]$ echo $0 -bash
关于 Login Shell
这部分定义,可以从 Bash
手册里看到:
1 2 A login shell is one whose first character of argument zero is a -, or one started with the --login option. # 登录 Shell 是 &0 的第一个字符是 - 或以 ——login 选项开头的 Shell。
可以看到 -
与 --login
两个条件二者取其一。
Login Shell 条件一
先看前面半句:first character of argument zero is a -
,即一个 Shell
的 argument zero
的第一个字符是个连字符 -。
在这里 argument zero
其实指的是 $0
。
注:$0 是 Shell 的一个参数,这个参数保存的是 bash 脚本的名称,bash 初始化的时候会设置 $0 这个变量。
1 2 [root@localhost ~]# echo $0 -bash
可以看到,打印出来脚本名称是 -bash
,第一个字符是 -,说明这个是一个 Login Shell
。
使用 ps
命令查看进程,也可以看到 bash
的进程名称为 -bash
:
1 2 3 4 [root@localhost ~]# ps -f UID PID PPID C STIME TTY TIME CMD root 5586 5578 0 00:32 pts/2 00:00:00 -bash root 5957 5586 0 00:43 pts/2 00:00:00 ps -f
1 2 3 4 5 6 [root@localhost ~]# bash [root@localhost ~]# ps -f UID PID PPID C STIME TTY TIME CMD root 2133 2125 0 20:50 pts/0 00:00:00 -bash root 2205 2133 0 20:52 pts/0 00:00:00 bash root 2238 2205 0 20:52 pts/0 00:00:00 ps -f
不过,这也不能说明这不是 Login Shell
还要看后半句。
Login Shell 条件二
Shell
启动的时候有 --login
参数,尝试一下:
1 2 3 4 5 6 7 8 9 10 11 [root@localhost ~]# bash --login [root@localhost ~]# ps -f UID PID PPID C STIME TTY TIME CMD root 2133 2125 0 20:50 pts/0 00:00:00 -bash root 2255 2133 0 20:53 pts/0 00:00:00 bash --login root 2287 2255 0 20:53 pts/0 00:00:00 ps -f [root@localhost ~]# logout [root@localhost ~]# ps -f UID PID PPID C STIME TTY TIME CMD root 2133 2125 0 20:50 pts/0 00:00:00 -bash root 2288 2133 0 20:54 pts/0 00:00:00 ps -f
可以使用 logout
命令,用 Docker
试一试:
1 2 3 4 5 6 7 [root@localhost ~]# docker run -it centos:7 /bin/bash --login [root@3341ceb0da2a /]# ps -f UID PID PPID C STIME TTY TIME CMD root 1 0 0 12:55 pts/0 00:00:00 /bin/bash --login root 19 1 0 12:55 pts/0 00:00:00 ps -f [root@3341ceb0da2a /]# logout [root@localhost ~]#
可以看到,如果带上 --login
,那它就是一个 Login shell
,可以使用 logout
退出 Shell
。
Login & Non-Login 区别
Login Shell
(登录 Shell
)和 Non-Login Shell
(非登录 Shell
)是两种不同类型的 Shell
环境,它们在启动时加载的配置文件以及行为方面存在一些区别。
Login Shell
(登录 Shell
):
当用户登录到系统时,通常会启动一个登录 Shell
。
登录 Shell
会加载一系列的配置文件,用于设置用户的环境(具体文件在第三章说明)。
Non-Login Shell
(非登录 Shell
):
当用户在已登录的会话中打开新的终端窗口或启动一个新的 Shell
时,会使用非登录 Shell
。
非登录 Shell
通常是为了执行用户的命令而启动的,不需要执行登录过程的设置。
非登录 Shell
也会加载一系列的配置文件,用于设置用户的环境(具体文件在第三章说明)。
Interactive & Non-Interactive
交互模式就是在终端上执行,Shell
等待你的输入,并且立即解释执行你提交的命令。
这种模式被称作交互式,是因为 Shell
与用户进行交互。
这种模式也是大多数用户非常熟悉的:登录、执行一些命令、退出,当你退出后,Shell
也终止了。
注:通常来说,Login Shell 都是 Interactive Shell。
同理,Shell
也可以运行在另外一种模式:非交互式模式。
非交互模式以 Shell Script
(非交互)方式执行。
在这种模式下,Shell
不与你进行交互,而是读取存放在文件中的命令,并且依此解释执行它们。
当它读到文件的结尾 Shell
也就终止了。
Interactive Shell
关于 Interactive Shell
这部分定义,可以从 Bash
手册里看到:
1 2 An interactive Shell is one started without non‐option arguments (unless -s is specified) and without the -c option, whose standard input and error are both connected to terminals (as determined by isatty(3)), or one started with the -i option. PS1 is set and $- includes i if bash is interactive, allowing a Shell script or a startup file to test this state. # 一个交互式 Shell 是在没有非选项参数(除非指定了 -s 选项)和 -c 选项的情况下启动的 Shell,其标准输入和错误都连接到终端,或者使用 -i 选项启动的Shell。如果 bash 是交互式的,PS1 被设置并且 $- 包含 i,允许 Shell 脚本或启动文件测试此状态。
上面那句话出现了一个单词:non‐option arguments
(非选项参数),这是什么?
option & argument
在 Linux
中,命令行使用空格分割为一个个的 argument
,例如:
以上图为例,命令执行时,在 ls
脚本的内部,可以使用 $0
来获取到 ls
这个字符串。
同理,$1
可以获取到 -l
,使用 $2
来获取到 -a
选项等等。
arguments
就是 $0
、$1
、$2
、…
,以空格分割命令行后的每个部分。
像 -l
和 -a
这种可以改变 ls
命令的行为的,叫做选项(即 option
),它们一般会被写在命令使用文档中:
那非选项参数是哪个?
上述命令中参数 /path/to/folder
既不是 -l
选项的值,也不是 -a
选项的值,它不属于任何一个 option
,所以它是一个 non-option arguments
。
Interactive Shell 条件一
看下手册里面的几个单词:PS1 is set
,这个 PS1
是啥?
$PS1
用于自定义命令提示符的外观和内容,以便增强 Shell
的可视化和交互性。
说明是个 Interactive Shell
的话,变量 $PS1
应该是有输出值的:
1 2 [root@localhost ~]# echo $PS1 [\u@\h \W]\$
使用 bash
命令的 -c
选项来执行上述命令:
注:bash -c 是在当前 Shell 进程中启动一个新的 Bash 子进程,并在该子进程中执行指定的命令或脚本。
1 2 [root@localhost ~]# bash -c "echo \$PS1"
可以理解为,执行 bash -c "echo \$PS1"
命令的时候,实际上由当前 Shell 启动了一个非交互式 Shell
,然后让这个非交互式去执行 echo $PS1
命令。
Interactive Shell 条件二
再看下手册里面的几个单词:$- includes i
,这个 $-
又是个啥?
$-
是一个特殊变量,用于表示当前 Shell
的选项和状态。
当 Bash Shell
是交互式的时候,$-
会包含字母 i
,表示 Shell
是交互式的。
说明是个 Interactive Shell
的话,变量 $-
的输出值应该包含 i
:
1 2 [root@localhost ~]# echo $- himBH
使用 bash
命令的 -c
选项来执行上述命令:
1 2 [root@localhost ~]# bash -c 'echo $-' hBc
Interactive Shell 条件三
前面已知了两个条件,还有一个容易被忽略的非选项参数。
既然已经知道了 non-option arguments
的含义,我们来简单验证一下:
1 2 3 4 5 6 7 8 [root@localhost ~]# cat demo # !/bin/bash echo $- [root@localhost ~]# bash demo hB [root@localhost ~]# bash [root@localhost ~]# echo $- himBH
还可以使用 bash -i
进行交互式 Shell
创建:
1 2 [root@localhost ~]# bash -i demo himB
注:bash -i 是在当前 Shell 进程中启动一个新的 Bash 子进程,并使该子进程成为一个交互式 Shell。
总结
\
Interactive Shell
Non-Interactive Shell
$PS1
$PS1
值为空只表示没有设置命令提示符,但并不足以确定 Shell
的交互性。 但 $PS1
存在一定可认为当前 Shell
是一个交互式 Shell
。
当前 Shell
是非交互式的,$PS1
的值一定为空。
$-
如果 $-
的值包含字母 i
,那么当前 Shell
是一个交互式 Shell
。
如果 $-
的值不包含字母 i
,那么当前 Shell
是一个非交互式 Shell
。
Non-argument
(排除 -i
参数)
没有非选项参数
有非选项参数
-i
、-c
-i
:开启一个交互式的子 Shell
去执行命令
-c
:开启一个非交互式的子 Shell
去执行命令
Linux Bash Env-Conf
这里我们可以想一个问题,以前我想的少,现在想得多了:
注:这里主要基于 Bash,其他 Shell 可能不适用。
Bash 配置文件调用逻辑
总结一下就是:
如果一个 Shell
是 Login Shell
时,/etc/profile
会被调用,随后按顺序查找 ~/.bash_profile
、~/.bash_login
、~/.profile
,并执行最先找到的一个。
如果是交互式的 Non-Login Shell
,则会调用 ~/.bashrc
。
如果是非交互式的 Non-Login Shell
,则会执行环境变量 $BASH_ENV
中的脚本(如果存在,我也没有实践过)。
特殊情况
当然,凡是无绝对,上面这些被调用的脚本可能去调用其它脚本。
例如:/etc/profile
可能去调用 /etc/profile.d/
目录中的脚本。
在 CentOS
中 ~/.bash_profile
会调用 ~/.bashrc
,查看下代码:
1 2 3 4 5 6 7 [root@localhost ~]# head -n 6 ~/.bash_profile # .bash_profile # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi
所以在 CentOS
中,Login Shell
的配置文件加载是这样的:
/etc/profile
~/.bash_profile
~/.bashrc
启动 Shell
时可以使用 --noprofile
、--norc
等选项使 Shell
不去调用对应的配置文件或者指定调用的配置文件,很少用到不展开讨论。
如果启动 Shell
时使用 sh
命令(sh
是 /bin/bash
的一个软链接)情况又不同,如下:
使用 .
或者 source
去执行一个脚本,是在当前 Shell
中执行脚本,不会启动新的 Shell
,所以不会去加载任何配置文件,没有上面这些流程。
注:
. 和 source 命令都用于在当前 Shell 环境中执行脚本,使脚本中的变量、函数、别名等定义在当前 Shell 中生效。
source 命令是 . 命令的一个 Bash 内建命令别名。