bash&shell系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.html


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

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

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

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

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

  1. [root@xuexi ~]# echo $-
  2. himBH
  3.  
  4. [root@xuexi ~]# vim a.sh
  5. #!/bin/bash
  6. echo $-
  7.  
  8. [root@xuexi ~]# bash a.sh
  9. hB

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

  1. [root@xuexi ~]# echo $PS1
  2. [\u@\h \W]\$

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

  1. [root@xuexi ~]# shopt login_shell
  2. login_shell on
  1. [root@xuexi ~]# bash
  2.  
  3. [root@xuexi ~]# shopt login_shell
  4. login_shell off

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

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

1.2 几种常见的bash启动方式

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

  1. [root@xuexi ~]# echo $PS1;shopt login_shell
  2. [\u@\h \W]\$
  3. login_shell on

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

  1. [root@xuexi ~]# su root
  2.  
  3. [root@xuexi ~]# echo $PS1;shopt login_shell
  4. [\u@\h \W]\$
  5. login_shell off
  1. [root@xuexi ~]# su -
  2. Last login: Sat Aug :: CST on pts/
  3.  
  4. [root@xuexi ~]# echo $PS1;shopt login_shell
  5. [\u@\h \W]\$
  6. login_shell on

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

  1. [root@xuexi ~]# bash
  2.  
  3. [root@xuexi ~]# echo $PS1;shopt login_shell
  4. [\u@\h \W]\$
  5. login_shell off
  1. [root@xuexi ~]# bash -l
  2.  
  3. [root@xuexi ~]# echo $PS1;shopt login_shell
  4. [\u@\h \W]\$
  5. login_shell on

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

  1. [root@xuexi ~]# (echo $BASH_SUBSHELL;echo $PS1;shopt login_shell)
  2.  
  3. [\u@\h \W]\$
  4. login_shell on
  1. [root@xuexi ~]# su
  2.  
  3. [root@xuexi ~]# (echo $BASH_SUBSHELL;echo $PS1;shopt login_shell)
  4.  
  5. [\u@\h \W]\$
  6. login_shell off

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

  1. [root@xuexi ~]# ssh localhost 'echo $PS1;shopt login_shell'
  2.  
  3. login_shell off

(6).执行shell脚本时,为非交互、非登录式shell。但指定了"--login"时,将为非交互、登录式shell。

例如,脚本内容如下:

  1. [root@xuexi ~]# vim b.sh
  2. #!/bin/bash
  3. echo $PS1
  4. shopt login_shell

不带"--login"选项时,为非交互、非登录式shell。

  1. [root@xuexi ~]# bash b.sh
  2.  
  3. login_shell off

带"--login"选项时,为非交互、登录式shell。

  1. [root@xuexi ~]# bash -l b.sh
  2.  
  3. login_shell on

(7).在图形界面下打开终端时,为交互式、非登录式shell。

但可以设置为使用交互式、登录式shell。

1.3 加载bash环境配置文件

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

bash环境配置文件主要有/etc/profile、~/.bash_profile、~/.bashrc、/etc/bashrc和/etc/profile.d/*.sh,为了测试各种情形读取哪些配置文件,先分别向这几个配置文件中写入几个echo语句,用以判断该配置文件是否在启动bash时被读取加载了。

  1. echo "echo '/etc/profile goes'" >>/etc/profile
  2. echo "echo '~/.bash_profile goes'" >>~/.bash_profile
  3. echo "echo '~/.bashrc goes'" >>~/.bashrc
  4. echo "echo '/etc/bashrc goes'" >>/etc/bashrc
  5. echo "echo '/etc/profile.d/test.sh goes'" >>/etc/profile.d/test.sh
  6. 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后缀的脚本。

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

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

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

  1. [root@xuexi ~]# grep -A \~/\.bashrc ~/.bash_profile
  2. if [ -f ~/.bashrc ]; then
  3. . ~/.bashrc
  4. fi

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

  1. [root@xuexi ~]# grep -A /etc/bashrc ~/.bashrc
  2. if [ -f /etc/bashrc ]; then
  3. . /etc/bashrc
  4. fi

其实/etc/bashrc中还有加载 /etc/profile.d/*.sh 的语句,但前提是非登录式shell时才会执行。以下是部分语句:

  1. if ! shopt -q login_shell ; then # We're not a login shell
  2. ...
  3. for i in /etc/profile.d/*.sh; do
  4. if [ -r "$i" ]; then
  5. if [ "$PS1" ]; then
  6. . "$i"
  7. else
  8. . "$i" >/dev/null 2>&1
  9. fi
  10. fi
  11. done
  12. ...
  13. fi

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

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

以下结果验证了结论:

  1. Last login: Mon Aug 14 04:49:29 2017 # 新开终端登录时
  2. /etc/profile.d/*.sh goes
  3. /etc/profile goes
  4. /etc/bashrc goes
  5. ~/.bashrc goes
  6. ~/.bash_profile goes
  1. [root@xuexi ~]# ssh localhost # ssh远程登录时
  2. root@localhost's password:
  3. Last login: Mon Aug 14 05:05:50 2017 from 172.16.10.1
  4. /etc/profile.d/*.sh goes
  5. /etc/profile goes
  6. /etc/bashrc goes
  7. ~/.bashrc goes
  8. ~/.bash_profile goes
  1. [root@xuexi ~]# bash -l # 执行带有"--login"选项的login时
  2. /etc/profile.d/*.sh goes
  3. /etc/profile goes
  4. /etc/bashrc goes
  5. ~/.bashrc goes
  6. ~/.bash_profile goes
  1. [root@xuexi ~]# su - # su带上"--login"时
  2. /etc/profile.d/*.sh goes
  3. /etc/profile goes
  4. /etc/bashrc goes
  5. ~/.bashrc goes
  6. ~/.bash_profile goes
  1. [root@xuexi ~]# vim a.sh # 执行shell脚本时带有"--login"时
  2. #!/bin/bash -l
  3. echo haha
  4.  
  5. [root@xuexi ~]# ./a.sh
  6. /etc/profile goes
  7. /etc/bashrc goes
  8. ~/.bashrc goes
  9. ~/.bash_profile goes
  10. haha

之所以执行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环境配置文件的实际过程为下图内方框中所示:

例如,执行不带"--login"的bash命令或su命令时。

  1. [root@xuexi ~]# bash
  2. /etc/profile.d/*.sh goes
  3. /etc/bashrc goes
  4. ~/.bashrc goes
  1. [root@xuexi ~]# su
  2. /etc/profile.d/*.sh goes
  3. /etc/bashrc goes
  4. ~/.bashrc goes

③.非交互式、非登录式shell启动bash时,不会加载前面所说的任何bash环境配置文件,但会搜索变量BASH_ENV,如果搜索到了,则加载其所指定的文件。但并非所有非交互式、非登录式shell启动时都会如此,见情况④。

它就像是这样的语句:

  1. if [ -n "$BASH_ENV" ];then
  2. . "$BASH_ENV"
  3. fi

几乎执行所有的shell脚本都不会特意带上"--login"选项,因此shell脚本不会加载任何bash环境配置文件,除非手动配置了变量BASH_ENV。

④.远程shell方式启动的bash,它虽然属于非交互、非登录式,但会加载~/.bashrc,所以还会加载/etc/bashrc,由于是非登录式,所以最终还会加载/etc/profile.d/*.sh,只不过因为是非交互式而使得执行的结果全部重定向到了/dev/null中。

如果了解rsync,就知道它有一种远程shell连接方式。所谓的远程shell方式,是指通过网络的方式启动bash并将bash的标准输出关联起来,就像它连接了一个远程的shell守护进程一样。一般由sshd实现这样的连接方式,老版的rshd也一样支持。

事实也确实如此,使用ssh连接但不登录远程主机时(例如只为了执行远程命令),就是远程shell的方式,但它却是非交互、非登录式的shell。

  1. [root@xuexi ~]# ssh localhost echo haha
  2. root@localhost's password:
  3. /etc/bashrc goes
  4. ~/.bashrc goes
  5. haha

正如上文所说,它同样加载了 /etc/profile.d/*.sh ,只不过/etc/bashrc中的if判断语句 if [ "$PS1" ]; then 使得非交互式的shell要将执行结果重定向到/dev/null中。

bash启动时加载配置文件过程的更多相关文章

  1. tomcat启动时加载配置文件 报错

    原因:  @serice("customerService")  和@Repository(value="customerDao")       解决: 直接@ ...

  2. 微服务架构 | *2.3 Spring Cloud 启动及加载配置文件源码分析(以 Nacos 为例)

    目录 前言 1. Spring Cloud 什么时候加载配置文件 2. 准备 Environment 配置环境 2.1 配置 Environment 环境 SpringApplication.prep ...

  3. Servlet在启动时加载的tomcat源码(原创)

    tomcat 8.0.36 知识点: 通过配置loadOnStartup可以设置Servlet是否在Tomcat启动时加载,以及按值大小进行有序加载,其最小有效值为0,最大有效值为Integer.MA ...

  4. ElasticSearch 启动时加载 Analyzer 源码分析

    ElasticSearch 启动时加载 Analyzer 源码分析 本文介绍 ElasticSearch启动时如何创建.加载Analyzer,主要的参考资料是Lucene中关于Analyzer官方文档 ...

  5. web.xml中配置启动时加载的servlet,load-on-starup

    web.xml中配置启动时加载的servlet,load-on-starup 使用servlet来初始化配置文件数据: 在servlet的配置当中,<load-on-startup>1&l ...

  6. spring项目中监听器作用-ContextLoaderListener(项目启动时,加载一些东西到缓存中)

    作用:在启动Web容器时,自动装配Spring applicationContext.xml的配置信息. 因为它实现了ServletContextListener这个接口,在web.xml配置这个监听 ...

  7. 设置程序启动时加载的storyboard

    这个设置表明:程序启动时会加载Main.storyboard

  8. Tomcat启动时加载数据到缓存---web.xml中listener加载顺序(例如顺序:1、初始化spring容器,2、初始化线程池,3、加载业务代码,将数据库中数据加载到内存中)

    最近公司要做功能迁移,原来的后台使用的Netty,现在要迁移到在uap上,也就是说所有后台的代码不能通过netty写的加载顺序加载了. 问题就来了,怎样让迁移到tomcat的代码按照原来的加载顺序进行 ...

  9. Tomcat启动时加载数据到缓存---web.xml中listener加载顺序(优先初始化Spring IOC容器)

    JavaWebSpringTomcatCache  最近用到在Tomcat服务器启动时自动加载数据到缓存,这就需要创建一个自定义的缓存监听器并实现ServletContextListener接口,并且 ...

随机推荐

  1. eclipse 设置 默认编码为 utf-8

    学习javaweb时,开发工具都采用utf-8的编码方式,给eclipse设置默认编码为utf-8的编码方法 菜单 Window -> preference -> General -> ...

  2. Java 异常处理笔记

    Java程序运行过程中所发生的异常事件可分为两类: §错误(Error):JVM系统内部错误.资源耗尽等严重情况 §违例(Exception): 其它因编程错误或偶然的外在因素导致的一般性问题,例如: ...

  3. HttpClient以json形式的参数调用http接口并对返回的json数据进行处理(可以带文件)

    1.参数的url就是被调用的地址,map是你要传的参数.参数转成json我使用的是gson方式转换的. 主要使用的jar包有httpclient-4.5.3.jar.httpcore-4.4.6.ja ...

  4. 【转载】接触Matlab10年后的一个总结,随时使用Matlab要掌握的一些要点

    来源: http://www.cnblogs.com/asxinyu/p/Basic_Matlab_Experience.html 接触Matlab10年后的一个总结,随时使用Matlab要掌握的一些 ...

  5. Certificates does not conform to algorithm constraints

    今天在开发时遇到一个新问题:Certificates does not conform to algorithm constraints,在此记录一下解决方案. 问题详情: [ERROR] Faile ...

  6. LuaFramework热更新过程(及可更新的loading界面实现)

          1.名词解释: 资源包:点击 LuaFramework  |  Build XXX(平台名) Resource,框架会自动将自定义指定的资源打包到StreamingAssets文件夹,这个 ...

  7. 一个爬取Bing每日壁纸的python脚本

    1. 背景 Bing搜索每天的背景图片有些比较适合做桌面,但是有的提供下载有的不提供下载.每天去点击下载又不太方便,所以第一次学习了一下python爬虫怎么写,写的很简单. 2. 相关技术 2.1 P ...

  8. plsql修改表字段alter

    场景:在生产过程中有时候需要不同的环境中修改表字段,使用sql语句比较方便! 1 演示 --添加字段的语法 alter table tablename add (column datatype [de ...

  9. ui-router ^1.x在ng1中使用state events

    官网信息:https://ui-router.github.io/ng1/docs/latest/modules/ng1_state_events.html Legacy state events P ...

  10. Android源码博文集锦4

    Android精选源码 一款常见的自定义加载动画 android开源记账项目CoCoin Android自定义view:拖拽选择按钮 Android指纹识别 一个折线图,它提供了几个非常实用的功能 一 ...