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. 【转载】Linux 文件系统的目录结构

    /bin 基础系统所需要的那些命令位于此目录,也是最小系统所需要的命令:比如 ls.cp.mkdir等命令:功能和/usr/bin类似,这个目录中的文件都是可执行的,普通用户都可以使用的命令.做为基础 ...

  2. Chrome

    一.简介 二.安装 1)离线版 http://www.google.cn/chrome/browser/thankyou.html?statcb=1&platform=win64&st ...

  3. 阅读Real-Time O(1) Bilateral Filtering 一文的相关感受。

    研究双边滤波有很长一段时间了,最近看了一篇Real-Time O(1) Bilateral Filtering的论文,标题很吸引人,就研读了一番,经过几天的攻读,基本已理解其思想,现将这一过程做一简单 ...

  4. Ajax与json

    Ajax Ajax简介 Ajax技术,从用户发送请求到获取响应,当用户界面在整个过程中不会受到干扰,而且我们可以在必要的时候只刷新页面的一小部分,而不用刷新整个页面,即"无刷新"技 ...

  5. web前端开发分享-css,js工具篇

    web前端开发乃及其它的相关开发,推荐sublime text, webstorm(jetbrains公司系列产品)这两个的原因在于,有个技术叫emmet, http://docs.emmet.io, ...

  6. file-loader及url-loader的使用

    file-loader主要用来处理图片,其实也可以在js和html及其他文件上,但很少那么使用,比如: require("file-loader?name=js/[hash].script. ...

  7. 手机网页Html代码实现(解决显示页面很小的问题)

    工作需要,要做一个手机自适应的网页效果,终于搞定,先分享并记录! 其实主要就是改掉HTML页面声明: 在网页中加入以下代码,就可以正常显示了: <meta name="viewport ...

  8. C/C++实践笔记 008

    逗号运算符优先级最低逗号运算符的值是最后一个的值int a=(1,2);执行结果为2逗号运算符每一条语句都要执行,执行方向从左向右 三目运算符表达式1?表达式2:表达式3 C语言里只有0和非0的区别 ...

  9. 【Jquery】$.Deferred 对象

    源:http://www.ruanyifeng.com/      deferred三个状态 “已完成”“未完成”“已失败”     (1) $.Deferred() 生成一个deferred对象.  ...

  10. 移动web开发—页面头部 META 总结

    meta指元素可提供有关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词. 标签位于文档的头部,不包含任何内容. 标签的属性定义了与文档相关联的名称/值对. ...