SHLVL 是记录多个 Bash 进程实例嵌套深度的累加器,而 BASH_SUBSHELL 是记录一个 Bash 进程实例中多个子 Shell(subshell)嵌套深度的累加器。

看不懂上面这句话不要紧,因为是我临时编的。其实如果你混淆了这两个变量,我猜你多半是对 BASH_SUBSHELL 这个变量名中的 subshell 概念不清,下面我们就讲讲什么是 subshell,什么不是。

很多人误以为在 Bash 里面再执行一次 Bash,或者再执行一个 Shell 脚本就是进入子 Shell 了,所以他们会有下面这样的疑问:

$ bash # 执行另外一个 bash 命令

$ echo $BASH_SUBSHELL

$ 0 # 怎么还是 0,我这不是在上个 Shell 的子 Shell 里吗?

$ echo ' echo $BASH_SUBSHELL ' > test; chmod +x test; ./test;

$ 0 # 难道再执行 Shell 脚本也不是子 Shell?

然而并不是,这些都不是子 Shell,这种情况只能描述成是“当前 Shell 启动了个外部命令,而这个外部命令刚好是个 Shell”,真正的子 Shell 是不需要重新执行硬盘上的外部命令的,全部是内存中的操作。上面这个示例中的 BASH_SUBSHELL 都应该替换成 SHLVL 才能看到累加效果。

有几本书给子 Shell 下过定义:

Advanced Bash-Scripting Guide 说:

A subshell is a child process launched by a shell (or shell script).

Bash Cookbook 说:

A subshell is a forked copy of the parent shell and shares it’s environment.

The Korn Shell: Unix & Linux Programming Manual 说:

A subshell is a separate copy of the parent shell, so variables, functions, and aliases from the parent shell are available to the subshell

第一本书流传较广,但它这句话说的太泛了,很容易误导人,虽然子 Shell 的确是当前 Shell 的子进程,但当前 Shell 的子进程不一定都是子 Shell(可能已经替换成了其他程序)。在 Bash 里面,只有特定的语法才会让代码进入子 Shell,比如管道两边的命令,比如用小括号括起来等等:

$ (echo $BASH_SUBSHELL)

1

$ ( ( ( ( (echo $BASH_SUBSHELL) ) ) ) )

5

真正的子 Shell 可以访问其父 Shell 的任何变量,而通过再执行一次 bash 命令所启动的 Shell 只能访问其父 Shell 传来的环境变量。这篇教程里面专门写了个例子:

For an example of the difference between a subshell and a child process that happens to be a shell:

unset a; a=1
(echo "a is $a in the subshell")
sh -c 'echo "a is $a in the child shell"'

In the subshell, the regular shell variable a is visible; but because it is not exported, the full child process does not see it.

上面的例子中把当前 Shell 执行外部命令 sh 启动的 Shell 叫做 child shell,可惜在中文里还是得翻译成子 Shell。。。

从 c 语言层面讲,真正的子 Shell 是当前 Shell 进程调用了 fork() 函数,在内存中复制出一个几乎一模一样的子进程。而执行 bash 命令启动的所谓 child shell 是在执行 fork() 函数的基础上,又执行了一次 execve() 函数,execve() 函数会重新加载硬盘上的 bash 命令并执行,替换刚才 fork 出来的那个 shell 进程,除了传入的环境变量外,是个崭新的进程。

总结一下就是说,SHLVL 变量是记录了所谓的 child shell 的嵌套深度,而 BASH_SUBSHELL 是记录了 subshell 的嵌套深度。

把 child shell 叫成子 Shell,在口头上说说还可以,因为中文里没有其它什么好的叫法用来指代它,但你心里得明白,这不是术语子 Shell 真正的含义。

写到这里,我想这篇文章的标题应该改成“什么是子 Shell”了。

SHLVL 和 BASH_SUBSHELL 两个变量的区别的更多相关文章

  1. mysql 存储过程中的declare 和 set @的两种变量的区别

    两者在手册中的说明: DECLARE var_name[,...] type [DEFAULT value]这个语句被用来声明局部变量.要给变量提供一个默认值,请包含一个DEFAULT子句.值可以被指 ...

  2. 网站开发进阶(六)JSP两种声明变量的区别

    JSP两种声明变量的区别 在JSP中用两种声明变量的方法,一种是在<%! %>内,一种是在<% %>内.他们之间有什么区别呢?我们直接看一个JSP文件来理解. 代码如下: &l ...

  3. jsp中两种include的区别【转】

    引用文章:http://www.ibm.com/developerworks/cn/java/j-jsp04293/ http://www.cnblogs.com/lazycoding/archive ...

  4. locale的设定及其LANG、LC_ALL、LANGUAGE环境变量的区别

    locale这个单词中文翻译成地区或者地域,其实这个单词包含的意义要宽泛很多.Locale是根据计算机用户所使用的语言,所在国家或者地区,以及当地的文化传统所定义的一个软件运行时的语言环境. [ora ...

  5. C#属性和变量的区别学习

    参考一   昨天看三层架构 (具体没怎么弄懂) 发现一般有一页代码专门用户存放字段的 而且都用用属性封装..令我诧异的是 很多字段属性都存在 get{}和set{} 和普通的变量没什么区别(可读可写) ...

  6. Ajax的get和post两种请求方式区别

    Ajax的get和post两种请求方式区别 (摘录):http://ip-10000.blog.sohu.com/114437748.html 解get和post的区别. 1. get是把参数数据队列 ...

  7. Linux set、env、declare、export显示shell变量的区别

    目录 Linux中 set.env.declare.export显示shell变量的区别 1. shell局部变量 2. 用户的环境变量 显示shell变量 declare 命令 export 命令 ...

  8. PHP 闭包获取外部变量和global关键字声明变量的区别

    最近在学习workerman的时候比较频繁的接触到回调函数,使用中经常会因为worker的使用方式不同,会用这两种不同的方式去调用外部的worker变量,这里就整理一下PHP闭包获取外部变量和glob ...

  9. java 类方法和实例方法 以及 类变量和实例变量 各种区别

    我们已经知道类体中的方法分为实例方法和类方法两种,用static修饰的是类方法.二者有什么区别呢?当一个类创建了一个对象后,这个对象就可以调用该类的方法. 当类的字节码文件被加载到内存时,类的实例方法 ...

随机推荐

  1. [WPF系列]-Deep Zoom

        参考 Deep Zoom in Silverlight

  2. Python学习记录day5

    title: Python学习记录day5 tags: python author: Chinge Yang date: 2016-11-26 --- 1.多层装饰器 多层装饰器的原理是,装饰器装饰函 ...

  3. OpenStack 企业私有云的若干需求(2):自动扩展(Auto-scaling) 支持

    本系列会介绍OpenStack 企业私有云的几个需求: 自动扩展(Auto-scaling)支持 多租户和租户隔离 (multi-tenancy and tenancy isolation) 混合云( ...

  4. PYTHON解析XML的多种方式效率对比实测

    在最初学习PYTHON的时候,只知道有DOM和SAX两种解析方法,但是其效率都不够理想,由于需要处理的文件数量太大,这两种方式耗时太高无法接受. 在网络搜索后发现,目前应用比较广泛,且效率相对较高的E ...

  5. Bootstrap 简介

    一.Bootstrap介绍 Bootstrap 是最受欢迎的 HTML.CSS 和 JS 框架,用于开发响应式布局.移动设备优先的 WEB 项目.本课时讲解 Bootstrap 的概念,并介绍 Boo ...

  6. ArrayList 保证多线程安全

    一:使用synchronized关键字 二:使用Collections.synchronizedList();使用方法如下: 假如你创建的代码如下:List<Map<String,Obje ...

  7. 第2章 Linux系统安装(3)_SSH连接Linux工具:SecureCRT和WinSCP

    4. SSH连接Linux工具 4.1 Linux网卡配置 (1)临时配置: ifconfig eth0 192.168.32.100 //给eth0网卡指定IP,写在ROM里的,关机会丢失. (2) ...

  8. ubuntu 14.04 以root权限启动chrome

    chrome版本 51.0.2704.103 How to run google chrome as root in linux - Unix & Linux Stack Exchange提示 ...

  9. iOS 3D Touch实践

    本文主要讲解3DTouch各种场景下的开发方法,开发主屏幕应用icon上的快捷选项标签(Home Screen Quick Actions),静态设置 UIApplicationShortcutIte ...

  10. css3伪类温故知新

    今天遇到一个问题.要求::after 和 ::before的 content:"content" 能够动态的进行变换,能被JavaScript操作. 百度了下,自己做了实验,发现有 ...