用户登录系统时,bash 会进行一系列操作,如加载各种 bash 配置文件,设置或清空一些列的变量,有时还会执行一些自定义的命令。
有些时候登录系统时可以交互的,如正常登录系统,有些时候时无交互的,如执行一个脚本。bash 启动类型分为交互式 shell 和非交互式 shell。交互式 shell 还分为交互式的登录 shell 和交互式的非登录 shell。
非交互式 shell 在某些时候可以在 bash 的 命令后面带上“--login|-l”,这样是非交互式的登录式 shell。
交互式模式就是在终端上执行,shell等待你的输入,并且立即执行你提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、退出。当你退出后,shell也终止了。
非交互式模式,以shell script(非交互)方式执行。在这种模式 下,shell不与你进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾EOF,shell也就终止了。
方法一:判断变量"-",如果值中包含字母“i”,表示交互式
echo $-
himBH
vim a.sh
#! /bin/bash
echo $-
bash a.sh
hB
方法二:判断变量 PS1,如果值非空,则为交互式,否则非非交互式,因为非交互式会清空该变量。
[root@localhost ~]# echo $PS1
[\u@\h \W]\$
登录式shell需要用户名、密码登录后才能进入的shell(或者通过"--login"选项生成的shell)。
非登录shell不需要输入用户名和密码即可打开的Shell,例如:直接命令“bash”就是打开一个新的非登录shell,在Gnome或KDE中打开一个“终端”(terminal)窗口程序也是一个非登录shell。
执行exit命令,退出一个shell(登录或非登录shell);
执行logout命令,退出登录shell(不能退出非登录shell)。
bash是 login shell时,其进程名为"-bash"而不是bash。
man bash: A login shell is one whose first character of argument zero is a -, or one started with the --login option.
# 在 login shell 中
[root@localhost ~]# echo $0
-bash
[root@localhost ~]# ps -ef | grep '\-bash' | grep -v grep
root 11338 11337 0 08:17 pts/0 00:00:00 -bash
#在非登录式 shell 中
[simon@localhost ~]$ echo $0
bash
[simon@localhost ~]$ ps -ef | grep '\-bash' | grep -v grep
shopt login\_shell
,值为 on 为登录式,否则为非登录式。
[root@localhost ~]# shopt login_shell
login_shell on
[root@localhost ~]# bash
login_shell off
判断交互式和登录式的情况,一句话命令:
[root@localhost ~]# echo $PS1;shopt login_shell
[root@localhost ~]# echo $-;shopt login_shell
伪终端登录,如 ssh 登录,或虚拟终端登录时,为交互式登录 shell
[root@localhost ~]# echo $PS1;shopt login_shell
[\u@\h \W]\$
login_shell on
执行 su 命令不带"--login"时为交互式非登录shell,带“--login”时,为交互式登录 shell。
执行不带"--login"选项的bash命令时为交互式、非登录式shell。但指定"--login"时,为交互式、登录式shell。
[root@localhost ~]# su root
[root@localhost ~]# echo $PS1;shopt login_shell
[\u@\h \W]\$
login_shell off
[root@localhost ~]# su -
Last login: Sat Aug 19 13:24:11 CST 2017 on pts/0
[root@loalhost ~]# echo $PS1;shopt login_shell
[\u@\h \W]\$
login_shell on
[root@localhsot ~]# bash
[root@loalhost ~]# echo $PS1;shopt login_shell
[\u@\h \W]\$
login_shell off
[root@localhost ~]# bash -l
[root@localhost ~]# echo $PS1;shopt login_shell
[\u@\h \W]\$
login_shell on
使用命令组合(使用括号包围命令列表)以及命令替换进入子 shell 时,继承父 shell 的交互和登录属性
[root@localhost ~]# (echo $BASH_SUBSHELL;echo $PS1;shopt login_shell)
1
[\u@\h \W]\$
login_shell on
[root@localhost ~]# su
[root@localhost ~]# (echo $BASH_SUBSHELL;echo $PS1;shopt login_shell)
1
[\u@\h \W]\$
login_shell off
ssh 执行远程命令,但不登录时,为非交互、非登录式。
[root@localhost ~]# ssh localhost 'echo $PS1;shopt login_shell'
login_shell off
执行 shell 脚本时,为非交互、非登录式 shell。执行--login 时,为非交互、登录式 shell。
[root@localhost ~]# vim b.sh
#!/bin/bash
echo $PS1
shopt login_shell
[root@localhost ~]# bash b.sh
login_shell off
[root@localhost ~]# bash -l b.sh
login_shell on
在图形界面下打开终端时,为交互式、非登录式 shell。不过可以在设置中设置为 login shell。
bash环境配置主要是通过加载bash环境配置文件来完成。根据shell是否交互、是否登陆,将会影响加载的配置文件,除了交互登录属性,有些特殊的属性也会影响读取配置文件。
bash 的环境配置文件主要有
为了测试各种情况读取哪些配置文件,先分别向几个配置文件中写入echo语句,用以判断该配置文件是否在启动bash时被读取加载。
echo "echo '/etc/profile goes'" >>/etc/profile
echo "echo '~/.bash_profile goes'" >> ~/.bash_profile
echo "echo '~/.bashrc goes'" >> ~/.bashrc
echo "echo '/etc/bashrc goes'" >> /etc/bashrc
echo "echo '/etc/profile.d/test.sh goes'" >> /etc/profile.d/test.sh
chmod +x /etc/profile.d/test.sh
bash 启动时,将先读取/etc/profile,在依次搜索~/.bash_profile、~/.bash_login和~/.profile,并仅加载第一个搜索到且可读的文件。当退出时,将执行~/.bash_logout中的命令。
在/etc/profile中有一条加载 `/etc/profile.d/*.sh` 的语句,它会使用source加载/etc/profile.d/下所有可执行的sh后缀的脚本。
grep -A 8 \*\.sh /etc/profile
for i in /etc/profile.d/*.sh ; do
if [ -r "$i" ]; then
if [ "${-#*i}" != "$-" ]; then # 将"$-"从左向右模式匹配"*i"并将匹配到的内容删除(即进行变量切分),如果"$-"切分后的值不等于"$-",则意味着是交互式shell
. "$i"
else
. "$i" >/dev/null 2>&1
fi
fi
done
在~/.bash_profile中也一样有加载~/.bashrc的命令。
grep -A 1 \~/\.bashrc ~/.bash_profile
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
~/.bashrc中又有加载/etc/bashrc的命令。
grep -A 1 /etc/bashrc ~/.bashrc
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
其实/etc/bashrc中还有加载 /etc/profile.d/*.sh 的语句,但前提是非登录式shell时才会执行。
if ! shopt -q login_shell ; then # We're not a login shell
...
for i in /etc/profile.d/*.sh; do
if [ -r "$i" ]; then
if [ "$PS1" ]; then
. "$i"
else
. "$i" >/dev/null 2>&1
fi
fi
done
...
fii
交互式的登录 shell 加载 bash 环境配置文件的实际过程如下:
验证:
新开终端登录时:
Last login: Sat Jan 26 08:32:46 2019
/etc/profile.d/test.sh goes
/etc/profile goes
/etc/bashrc goes
~/.bashrc goes
~/.bash_profile goes
ssh 远程登录时:
ssh localhost
Last login: Sat Jan 26 09:58:20 2019 from 192.168.1.110
/etc/profile.d/test.sh goes
/etc/profile goes
/etc/bashrc goes
~/.bashrc goes
~/.bash_profile goes
执行带有“--login”选项的 login:
bash -l
/etc/profile.d/test.sh goes
/etc/profile goes
/etc/bashrc goes
~/.bashrc goes
~/.bash_profile goes
su 带上“--login”时:
[root@localhost ~]# su -
上一次登录:六 1月 26 10:00:10 CST 2019从 localhostpts/2 上
/etc/profile.d/test.sh goes
/etc/profile goes
/etc/bashrc goes
~/.bashrc goes
~/.bash_profile goes
执行 shell 脚本时,带有"--login"时:
vim a.sh
#!/bin/bash --login
echo $-
echo Yeah
[root@localhost ~]# ./a.sh
/etc/profile goes
/etc/bashrc goes
~/.bashrc goes
~/.bash_profile goes
hB
Yeah
之所以执行shell脚本时没有显示执行 /etc/profile.d/*.sh ,是因为它是非交互式的,根据/etc/profile中的 if["${-#*i}"!="$-"] 判断,它将会把 /etc/profile.d/*.sh 的执行结果重定向到/dev/null中。也就是说,即使是shell脚本(带"--login "选项),它也加载了所有bash环境配置文件。
总结一下:
读取~/.bashrc,不会读取/etc/profile 和~/.bash_profile、~/.bash_login和~/.profile
验证:
[root@localhost ~]# bash
/etc/profile.d/test.sh goes
/etc/bashrc goes
~/.bashrc goes
[root@localhost ~]# su
/etc/profile.d/test.sh goes
/etc/bashrc goes
~/.bashrc goes
启动 bash 时,不会加载任何 bash 环境配置文件,但会搜索变量BASH_ENV。如果搜索到,则加载其所指定的文件。但并非所有非交互式,非登录式 shell 启动时都会如此。
几乎执行所有的shell脚本都不会特意带上"--login"选项,因此shell脚本不会加载任何bash环境配置文件,除非手动配置了变量BASH_ENV。
虽然属于非交互非登录式,但会加载~/.bashrc,所以还会加载/etc/bashrc,由于是非登录式,所以最终还会加载/etc/profile.d/*.sh,只不过因为是非交互式而使得执行的结果全部重定向到了/dev/null中。
[root@localhost ~]# ssh localhost echo hahaha
root@localhost's password:
/etc/bashrc goes
~/.bashrc goes
hahaha
它同样加载了 /etc/profile.d/*.sh ,只不过/etc/bashrc中的if判断语句 if["$PS1"];then 使得非交互式的shell要将执行结果重定向到/dev/null中。
我们可以把bash配置文件分为两种,一种是全局配置文件,一种是在用户家目录的个人配置文件。
profile 类文件的作用:定义环境变量、运行命令或脚本
bashrc 类文件的作用:定义本地变量和函数、命令别名
先加载的配置文件的配置,可能会被后加载的配置所覆盖。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有