如何检测子Shell和子进程
上节我们说了子 Shell 和子进程的区别,这节就来看一下如何检测它们。
我们都知道使用 $ 变量可以获取当前进程的 ID,我在父 Shell 和子 Shell 中都输出 $ 的值,只要它们不一样,不就是创建了一个新的进程吗?那我们就来试一下吧。
其实不是我骗人,而是你掉坑里了,因为 $ 变量在子 Shell 中无效!Base 官方文档说,在普通的子进程中,$ 确实被展开为子进程的 ID;但是在子 Shell 中,$ 却被展开成父进程的 ID。
除了 $,Bash 还提供了另外两个环境变量——SHLVL 和 BASH_SUBSHELL,用它们来检测子 Shell 非常方便。
SHLVL 是记录多个 Bash 进程实例嵌套深度的累加器,每次进入一层普通的子进程,SHLVL 的值就加 1。而 BASH_SUBSHELL 是记录一个 Bash 进程实例中多个子 Shell(sub shell)嵌套深度的累加器,每次进入一层子 Shell,BASH_SUBSHELL 的值就加 1。
1) 我们还是用实例来说话吧,先说 SHLVL。创建一个脚本文件,命名为 test.sh,内容如下:
仔细观察的读者应该会发现,使用 bash 命令开启新的会话后,需要使用 exit 命令退出才能回到上一级 Shell 会话。
2) 再说一下 BASH_SUBSHELL,请看下面的命令:
注意,“进程替换”看起来好像产生了一个子 Shell,其实只是玩了一个障眼法而已。进程替换只是借助文件在
我们不妨来实际检测一下:
我们都知道使用 $ 变量可以获取当前进程的 ID,我在父 Shell 和子 Shell 中都输出 $ 的值,只要它们不一样,不就是创建了一个新的进程吗?那我们就来试一下吧。
[mozhiyan@localhost ~]$ echo $$ #父Shell PID 3299 [mozhiyan@localhost ~]$ (echo $$) #组命令形式的子Shell PID 3299 [mozhiyan@localhost ~]$ echo "https://www.xinbaoku.com" | { echo $$; } #管道形式的子Shell PID 3299 [mozhiyan@localhost ~]$ read < <(echo $$) #进程替换形式的子Shell PID [mozhiyan@localhost ~]$ echo $REPLY 3299你看,子 Shell 和父 Shell 的 ID 都是一样的,哪有产生新进程了?作者你是不是骗人呢?
其实不是我骗人,而是你掉坑里了,因为 $ 变量在子 Shell 中无效!Base 官方文档说,在普通的子进程中,$ 确实被展开为子进程的 ID;但是在子 Shell 中,$ 却被展开成父进程的 ID。
除了 $,Bash 还提供了另外两个环境变量——SHLVL 和 BASH_SUBSHELL,用它们来检测子 Shell 非常方便。
SHLVL 是记录多个 Bash 进程实例嵌套深度的累加器,每次进入一层普通的子进程,SHLVL 的值就加 1。而 BASH_SUBSHELL 是记录一个 Bash 进程实例中多个子 Shell(sub shell)嵌套深度的累加器,每次进入一层子 Shell,BASH_SUBSHELL 的值就加 1。
1) 我们还是用实例来说话吧,先说 SHLVL。创建一个脚本文件,命名为 test.sh,内容如下:
#!/bin/bash echo "$SHLVL $BASH_SUBSHELL"然后打开 Shell 窗口,依次执行下面的命令:
[mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 2 0 [mozhiyan@localhost ~]$ bash #执行bash命令开启一个新的Shell会话 [mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 3 0 [mozhiyan@localhost ~]$ bash ./test.sh #通过bash命令运行脚本 4 0 [mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 3 0 [mozhiyan@localhost ~]$ chmod +x ./test.sh #给脚本增加执行权限 [mozhiyan@localhost ~]$ ./test.sh 4 0 [mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 3 0 [mozhiyan@localhost ~]$ exit #退出内层Shell exit [mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 2 0SHLVL 和 BASH_SUBSHELL 的初始值都是 0,但是输出结果中 SHLVL 的值从 2 开始,我猜测 Bash 在初始化阶段可能创建了子进程,我们暂时不用理会它,将关注点放在值的变化上。
仔细观察的读者应该会发现,使用 bash 命令开启新的会话后,需要使用 exit 命令退出才能回到上一级 Shell 会话。
bash ./test.sh
和chmod +x ./test.sh; ./test.sh
这两种运行脚本的方式,在脚本运行期间会开启一个子进程,运行结束后立即退出子进程。2) 再说一下 BASH_SUBSHELL,请看下面的命令:
[mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 2 0 [mozhiyan@localhost ~]$ (echo "$SHLVL $BASH_SUBSHELL") #组命令 2 1 [mozhiyan@localhost ~]$ echo "hello" | { echo "$SHLVL $BASH_SUBSHELL"; } #管道 2 1 [mozhiyan@localhost ~]$ var=$(echo "$SHLVL $BASH_SUBSHELL") #命令替换 [mozhiyan@localhost ~]$ echo $var 2 1 [mozhiyan@localhost ~]$ ( ( ( (echo "$SHLVL $BASH_SUBSHELL") ) ) ) #四层组命令 2 4你看,组命令、管道、命令替换这几种写法都会进入子 Shell。
注意,“进程替换”看起来好像产生了一个子 Shell,其实只是玩了一个障眼法而已。进程替换只是借助文件在
()
内部和外部的命令之间传递数据,但是它并没有创建子 Shell;换句话说,()
内部和外部的命令是在一个进程(也就是当前进程)中执行的。我们不妨来实际检测一下:
[mozhiyan@localhost ~]$ echo "$SHLVL $BASH_SUBSHELL" 2 0 [mozhiyan@localhost ~]$ echo "hello" > >(echo "$SHLVL $BASH_SUBSHELL") 2 0SHLVL 和 BASH_SUBSHELL 变量的值都没有发生改变,说明进程替换既没有进入子进程,也没有进入子 Shell。
所有教程
- C语言入门
- C语言编译器
- C语言项目案例
- 数据结构
- C++
- STL
- C++11
- socket
- GCC
- GDB
- Makefile
- OpenCV
- Qt教程
- Unity 3D
- UE4
- 游戏引擎
- Python
- Python并发编程
- TensorFlow
- Django
- NumPy
- Linux
- Shell
- Java教程
- 设计模式
- Java Swing
- Servlet
- JSP教程
- Struts2
- Maven
- Spring
- Spring MVC
- Spring Boot
- Spring Cloud
- Hibernate
- Mybatis
- MySQL教程
- MySQL函数
- NoSQL
- Redis
- MongoDB
- HBase
- Go语言
- C#
- MATLAB
- JavaScript
- Bootstrap
- HTML
- CSS教程
- PHP
- 汇编语言
- TCP/IP
- vi命令
- Android教程
- 区块链
- Docker
- 大数据
- 云计算