本文的环境: Ubuntu 18.04 Server + Bash 4.4.20
bash启动时会执行一系列脚本, 具体要执行哪些启动文件, 这和bash的类型有关: 是否为交互式(interactive)的shell, 是否为登录式(login)的shell
bash script.sh
或./script.sh
执行脚本时, 会创建一个子shell来执行此脚本, 此时的shell为非交互式的$-
会输出set设置的一些选项, 输出结果结果中的i
表示interactive(但实际上不能通过set设置是否为交互式)
case "$-" in *i*) echo 'This shell is interactive' ;; *) echo 'This shell is not interactive' ;; esac
$ echo $- himBHs $ cat script.sh echo "$-" $ bash script.sh hB
在非交互式shell不会设置PS1, 所以通过PS1是否有值判断也是可行的
if [ -z "$PS1" ]; then echo 'This shell is not interactive' else echo 'This shell is interactive' fi
通过bash --login
或su -l
命令启动的为登录式shell
通过ssh连接的为登录式shell
通过bash
命令启动的为非登录式shell
通过ssh远程执行命令的为非登录式shell
图形化界面下启动的"terminal"默认为非登录式的, 但是可以更改为登录式shell
可通过shopt
命令来查看是否为登录式shell, 也可以通过此命令来转换登录式/非登录式shell
$ shopt login_shell login_shell off
了解了什么是交互式/登录式shell之后, 我们来看下这2*2种情况下shell的初始化文件
涉及到的文件主要有: /etc/profile, /etc/bash.bashrc, ~/.profile, ~/.bashrc, ~/.bash_logout
准备两个用户, 因为需要切换用户
将echo "$(date '+%H:%M:%S:%N') $(whoami) /etc/profile" >>/home/ba/log
添加到上述五个文件的第一行(后三个文件第二个用户也要修改)
由于涉及到.bash_logout文件, 此文件是在退出bash的时候才执行, 如果只连接一次服务器, 推出后再登录看日志, 会把第二次登录的结果也带到日志中, 不太严谨
所以应该开两个窗口连上服务器, 第一个窗口执行tail -f /home/ba/log
观察日志, 第二个窗口可以随意登录推出和执行命令
交互登录式shell初始化的文件为:
通过ssh的方式登录即可得到交互登录式shell
$ ssh ba@...... $ logout # log ### /etc/profile文件中有 . /etc/bash.bashrc 的语句, 所以/etc/bash.bashrc也被执行了 ### 同样, ~/.profile中也有 . ~/.bashrc 的语句 ### 注意: 不同的发行版, 不同的shell, 默认情况下对于/etc/bash.bashrc可能会存在差异, 有些bash是通过~/.bashrc来执行 . /etc/bash.bashrc 13:35:47:304181923 ba /etc/profile 13:35:47:309532807 ba /etc/bash.bashrc 13:35:47:353487400 ba ~/.profile 13:35:47:358734199 ba ~/.bashrc 13:36:14:812344118 ba ~/.bash_logout
再来验证~/.bash_profile, ~/.bash_login, and ~/.profile这三个文件的优先级
$ ssh ba@...... $ vim .bash_profile $ cat .bash_profile echo "$(date '+%H:%M:%S:%N') $(whoami) ~/.bash_profile" >>/home/ba/log $ logout # log ### ~/.profile连带着~/.bashrc都没有了, 取而代之的是~/.bash_profile 13:53:07:178921031 ba /etc/profile 13:53:07:183184595 ba /etc/bash.bashrc 13:53:07:211623026 ba ~/.bash_profile 13:53:12:253835053 ba ~/.bash_logout
执行完成之后, 我删除了~/.bash_profile, 后续章节都不涉及~/.bash_profile
交互非登录式shell初始化的文件为:
通过su
命令切换用户即可得到交互非登录式shell
$ ssh ba@...... $ su root $ echo $- himBHs $ shopt login_shell login_shell off # log 14:04:53:515775025 root /etc/bash.bashrc 14:04:53:522999498 root ~/.bashrc
通过su
切换用户后, 按下Tab键发现自动补全用不了, 是因为/etc/profile中有以下一段, 而自动补全与/etc/profile.d中的脚本有关, 将以下内容移动到/etc/bash.bashrc或~/.bashrc, 即可保证交互式shell可以完成自动补全
if [ -d /etc/profile.d ]; then for i in /etc/profile.d/*.sh; do if [ -r $i ]; then . $i fi done unset i fi
非交互非登录式shell初始化的文件为:
$BASH_ENV
变量并执行, 就像if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
一样bash xxx.sh
所创建用来执行脚本的子shell为非登录非交互式shell
$ ssh ba@...... $ cat script.sh echo "$-" echo "$BASH_ENV" shopt login_shell $ bash script.sh hB login_shell off # log 无输出
设置$BASH_ENV
变量
$ export BASH_ENV='~/.bashrc' $ bash script.sh hB ~/.bashrc login_shell off # log 13:57:00:355294304 ba ~/.bashrc
非交互登录式shell初始化的文件为:
$BASH_ENV
变量并执行, 就像if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
一样在bash xxx.sh
的基础上加上--login
选项可得到登录非交互式shell
$ ssh ba@...... $ bash --login script.sh hB login_shell on # log 13:59:14:886387539 ba /etc/profile 13:59:14:894341293 ba ~/.profile 13:59:14:897853694 ba ~/.bashrc
虽然此处也是登录式shell, 为何没有/etc/bash.bashrc? 因为在/etc/profile中有如下一段, 只有在交互式shell才执行/etc/bash.bashrc
if [ "${PS1-}" ]; then ... . /etc/bash.bashrc fi
再验证下$BASH_ENV
. 注意, .bashrc输出了两次, 一次是.profile调用, 一次是$BASH_ENV
的作用
$ export BASH_ENV='~/.bashrc' $ bash --login script.sh hB ~/.bashrc login_shell on # log 14:03:35:455306977 ba /etc/profile 14:03:35:461542280 ba ~/.profile 14:03:35:465203982 ba ~/.bashrc 14:03:35:469716368 ba ~/.bashrc