• bash启动时加载配置文件过程


    当用户登录系统时,会加载各种bash配置文件,还会设置或清空一系列变量,有时还会执行一些自定义的命令。这些行为都算是启动bash时的过程。

    另外,有些时候登录系统是可以交互的(如正常登录系统),有些时候是无交互的(如执行一个脚本),因此总的来说bash启动类型可分为交互式shell和非交互式shell。更细分一层,交互式shell还分为交互式的登录shell和交互式非登录shell,非交互的shell在某些时候可以在bash命令后带上"--login"或短选项"-l",这时也算是登录式,即非交互的登录式shell。

    判断是否交互式、是否登录式                        

    判断是否为交互式shell有两种简单的方法:

    方法一:判断变量"-",如果值中含有字母"i",表示交互式。

    [root@localhost ~]# echo $-
    himBH
    [root@localhost ~]# vim a.sh
    #!/bin/bash
    echo $-
    [root@localhost ~]# bash a.sh 
    hB

    方法二:判断变量PS1,如果值非空,则为交互式,否则为非交互式,因为非交互式会清空该变量。

    [root@localhost ~]# echo $PS1
    [u@h W]$

    判断是否为登录式的方法也很简单,只需执行"shopt login_shell"即可。值为"on"表示为登录式,否则为非登录式。

    [root@localhost ~]# shopt login_shell  
    login_shell     on
    [root@localhost ~]# bash
    [root@localhost ~]# shopt login_shell
    login_shell     off

    所以,要判断是交互式以及登录式的情况,可简单使用如下命令:

    echo $PS1;shopt login_shell
    # 或者
    echo $-;shopt login_shell

    几种常见的bash启动方式                    

    (1).正常登录(伪终端登录如ssh登录,或虚拟终端登录)时,为交互式登录shell。

    [root@localhost ~]# echo $PS1;shopt login_shell 
    [u@h W]$
    login_shell     on

    (2).su命令,不带"--login"时为交互式、非登录式shell,带有"--login"时,为交互式、登录式shell。

    [root@localhost ~]# su - root
    [root@localhost ~]# echo $PS1;shopt login_shell 
    [u@h W]$
    login_shell     on

    (3).执行不带"--login"选项的bash命令时为交互式、非登录式shell。但指定"--login"时,为交互式、登录式shell。

    [root@localhost ~]# echo $PS1;shopt login_shell
    [u@h W]$
    login_shell     on

    (4).使用命令组合(使用括号包围命令列表)以及命令替换进入子shell时,继承父shell的交互和登录属性。

    [root@localhost ~]# (echo $BASH_SUBSHELL;echo $PS1;shopt login_shell)
    1
    [u@h W]$
    login_shell     on

    (5).ssh执行远程命令,但不登录时,为非交互、非登录式。

    [root@localhost ~]# ssh localhost 'echo $PS1;shopt login_shell'
    The authenticity of host 'localhost (::1)' can't be established.
    RSA key fingerprint is 3d:68:48:4c:16:55:da:9e:54:10:29:34:3e:6a:61:fb.
    Are you sure you want to continue connecting (yes/no)? yes
    Warning: Permanently added 'localhost' (RSA) to the list of known hosts.
    root@localhost's password: 
    
    login_shell     off

    加载bash环境配置文件                        

    无论是否交互、是否登录,bash总要配置其运行环境。bash环境配置主要通过加载bash环境配置文件来完成。但是否交互、是否登录将会影响加载哪些配置文件,除了交互、登录属性,有些特殊的属性也会影响读取配置文件的方法。

    bash环境配置文件主要有/etc/profile、~/.bash_profile、~/.bashrc、/etc/bashrc和/etc/profile.d/*.sh,为了测试各种情形读取哪些配置文件,先分别向这几个配置文件中写入几个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

    ①.交互式登录shell或非交互式但带有"--login"(或短选项"-l",例如在shell脚本中指定"#!/bin/bash -l"时)的bash启动时,将先读取/etc/profile,再依次搜索~/.bash_profile、~/.bash_login和~/.profile,并仅加载第一个搜索到且可读的文件。当退出时,将执行~/.bash_logout中的命令。

    但要注意,在/etc/profile中有一条加载 /etc/profile.d/*.sh 的语句,它会使用source加载/etc/profile.d/下所有可执行的sh后缀的脚本。

    [root@localhost ~]# grep -A 8 *.sh /etc/profile  
    for i in /etc/profile.d/*.sh ; do
        if [ -r "$i" ]; then
            if [ "${-#*i}" != "$-" ]; then
                . "$i"
            else
                . "$i" >/dev/null 2>&1
            fi
        fi
    done

    内层if语句中的 "${-#*i}" != "$-" 表示将"$-"从左向右模式匹配"*i"并将匹配到的内容删除(即进行变量切分),如果"$-"切分后的值不等于"$-",则意味着是交互式shell,于是怎样怎样,否则怎样怎样。

    同样的,在~/.bash_profile中也一样有加载~/.bashrc的命令。

    [root@localhost ~]#  grep -A 1 ~/.bashrc ~/.bash_profile
    if [ -f ~/.bashrc ]; then
            . ~/.bashrc
    fi

    而~/.bashrc中又有加载/etc/bashrc的命令。

    [root@localhost ~]# 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
    ...
    fi

    从内层if语句和/etc/profile中对应的判断语句的作用是一致的,只不过判断方式不同,写法不同。

    因此,交互式的登录shell加载bash环境配置文件的实际过程如下图:

    以下结果验证了结论:

    Last login: Mon Aug 14 04:49:29 2017     # 新开终端登录时
    /etc/profile.d/*.sh goes
    /etc/profile goes
    /etc/bashrc goes
    ~/.bashrc goes
    ~/.bash_profile goes
    [root@localhost ~]# ssh localhost
    The authenticity of host 'localhost (::1)' can't be established.
    ECDSA key fingerprint is SHA256:ADR6o4CtLxt00cZpKBtPwsloQD6SsAA/78Drd+UKopo.
    ECDSA key fingerprint is MD5:4d:a4:85:4c:4b:c0:b3:31:93:94:21:65:d4:c5:b2:c4.
    Are you sure you want to continue connecting (yes/no)? yes
    Warning: Permanently added 'localhost' (ECDSA) to the list of known hosts.
    root@localhost's password: 
    Last login: Fri Dec  6 19:53:41 2019 from 192.168.1.5
    /etc/profile.d/*.sh goes
    /etc/profile goes
    /etc/bashrc goes
    ~/.bashrc goes
    ~/.bash_profile goes

    之所以执行shell脚本时没有显示执行 /etc/profile.d/*.sh ,是因为它是非交互式的,根据/etc/profile中的 if [ "${-#*i}" !="$-" ] 判断,它将会把 /etc/profile.d/*.sh 的执行结果重定向到/dev/null中。也就是说,即使是shell脚本(带"--login "选项),它也加载了所有bash环境配置文件。

    ②.交互式非登录shell的bash启动时,将读取~/.bashrc,不会读取/etc/profile和~/.bash_profile、~/.bash_login和~/.profile。

    因此,交互式非登录shell加载bash环境配置文件的实际过程为下图内方框中所示:

  • 相关阅读:
    C# 5.0 CallerMemberName CallerFilePath CallerLineNumber 在.NET4中的使用
    Protocol Buffers 语法指南
    ERP、SCM及电子商务关系分析
    ]进程注入是王道之为NhibernateProfiler增加“附加到进程”功能原理(源码)
    架构师职位与软件文档的思考
    OSGI:从面向接口编程来理解OSGI
    开源的.NET桌面程序自动更新组件 ——Sharp Updater 2.1发布
    C#开源文件实时监控工具Tail&TailUI
    SQL 存储过程入门(变量)
    Python入门笔记(2):基础(上)
  • 原文地址:https://www.cnblogs.com/liujunjun/p/11997638.html
Copyright © 2020-2023  润新知