子shell

子shell的概念贯穿整个shell,写shell脚本时更是不可不知。所谓子shell,即从当前shell环境新开一个shell环境,这个新开的shell环境就称为子shell(subshell),而开启子shell的环境称为该子shell的父shell。子shell和父shell的关系其实就是子进程和父进程的关系,只不过子shell和父shell所关联的进程是bash进程。

子shell会从父shell中继承很多环境,如变量、命令全路径、文件描述符、当前工作目录、陷阱等等,但子shell有很多种类型,不同类型的子shell继承的环境不相同。可以使用$BASH_SUBSHELL变量来查看从当前进程开始的子shell层数,$BASHPID查看当前所处BASH的PID,这不同于特殊变量"$$"值,因为"$$"在大多数情况下都会从父shell中继承。

Linux上创建子进程的方式有三种:一种是fork出来的进程,一种是exec出来的进程,一种是clone出来的进程。此处无需关心clone,因为它用来实现Linux中的线程。

(1).fork是复制进程,它会复制当前进程的副本(不考虑写时复制的模式),以适当的方式将这些资源交给子进程。所以子进程掌握的资源和父进程是一样的,包括内存中的内容,所以也包括环境变量和变量。但父子进程是完全独立的,它们是一个程序的两个实例。

(2).exec是加载另一个应用程序,替代当前运行的进程,也就是说在不创建新进程的情况下加载一个新程序。exec还有一个动作:在进程执行完毕后,退出exec所在的shell环境。

所以为了保证进程安全,若要形成新的且独立的子进程,都会先fork一份当前进程,然后在fork出来的子进程上调用exec来加载新程序替代该子进程。例如在bash下执行cp命令,会先fork出一个bash,然后再exec加载cp程序覆盖子bash进程变成cp进程。

再来说明子shell的问题。一般fork出来的子进程,内容和父进程是一样的(包括变量),例如执行cp命令时也能获取到父进程的变量。但是cp命令在哪里执行呢?执行cp命令敲入回车后,当前的bash进程fork出一个子bash,然后子bash通过exec加载cp程序替代子bash。这算是进入了子shell吗?更通用的问题是:什么情况下会进入子shell环境,什么时候不进入子shel环境呢?

判断是否进入了子shell的方式非常简单,执行"echo $BASHPID",如果该值和父bash进程的pid值不同,则表示进入了子shell。在shell中是否进入子shell的情况可以分为几种:

①.执行bash内置命令时。

②.执行bash命令本身时。

显然它会进入子shell环境,它的绝大多数环境都是新配置的,因为会加载一些环境配置文件。事实上fork出来的bash子进程内容完全继承父shell,但因重新加载了环境配置项,所以子shell没有继承普通变量,更准确的说是覆盖了从父shell中继承的变量。

其实执行bash命令,既可以认为进入了子shell,也可以认为没有进入子shell。在执行bash命令后从变量$BASH_SUBSHELL的值为0可以认为它没有进入子shell。但从执行bash命令后进入了新的shell环境来看,它有其父bash进程,且$BASHPID值和父shell不同,所以它算是进入了子shell。

其实,执行bash命令更应该被认为是进入了一个完全独立的、全新的shell环境,而不应该认为是进入了片面的子shell环境。

此外,执行bash命令,"$$"不会继承父shell的值。

③.执行shell脚本时。

脚本中第一行总是"#!/bin/bash"或者直接"bash xyz.sh",这和上面的执行bash进入子shell其实是一回事,都是使用bash命令进入子shell。只不过此时的bash命令和情况②中直接执行bash命令所隐含的选项不一样,所以继承和加载的shell环境也不一样。事实也确实如此,它仅只继承父shell的某些环境变量,其余环境一概初始化。

另外,执行shell脚本相比于直接执行bash命令,还多了一个动作:脚本执行完毕后自动退出子shell。

此外,shell脚本中的"$$"不继承父shell的值。

④.执行shell函数时。

其实shell函数就是命令,它和bash内置命令的情况一样。直接执行时不会进入子shell,但放在管道后会进入子shell。

⑤.执行非bash内置命令时。

例如执行cp命令、grep命令等,它们直接fork一份bash进程,然后使用exec加载程序替代该子bash。此类子进程会继承所有父bash的环境。但严格地说,这已经不是子shell,因为exec加载的程序已经把子bash进程替换掉了,这意味着丢失了很多bash环境。在bash文档中,直接称呼这种环境为"单独的环境",和子shell的概念类似。

⑥.命令替换。

当命令行中包含了命令替换部分时,将开启一个子shell先执行这部分内容,再将执行结果返回给当前命令。因为这次的子shell不是通过bash命令进入的子shell,所以它会继承父shell的所有变量内容。这也就解释了"echo $(echo $$)"中"$$"的结果是当前bash的pid号,而不是子shell的pid号,但"echo $(echo $BASHPID)"却和父bash进程的pid不同,因为它不是使用bash命令进入的子shell。

⑦.使用括号()组合一系列命令。

例如(ls;date;echo haha),独立的括号将会开启一个子shell来执行括号内的命令。这种情况等同于情况⑤。

⑧.放入后台运行的任务

它不仅是一个独立的子进程,还是在子shell环境中运行的。例如"echo hahha &"。

⑨.进程替换

既然是新进程了,当然进入子shell执行。例如"cat <(echo haha)"。

再说明"$$"的继承问题。除了直接执行bash命令和shell脚本这两种子shell,其他进入子shell的情况都会继承父shell的值。前面也已经说了,其实shell脚本和直接执行bash命令开启子shell的方式是一样的,它们都不会继承"$$"值,可以根据上述实验自行测试。

需要说明的是,子shell的环境设置不会粘滞到父shell环境,无论是使用export还是source,它们都只能的概念都是父shell到子shell,不是也不会是从子shell到父shell。也就是说子shell的变量等不会影响父shell。

最后,建议同时阅读另一篇文章:bash启动时环境配置流程,此文中详细解释了bash启动时加载哪些配置文件。

知道了子shell的概念,想必对shell解释器和shell就理解的差不多。于是,下面这张经典的图中为什么出现shell层也很容易理解。说白了,SHELL就是提供了执行环境和解析了命令,并将解析后的命令交给kernel,使其在给定的环境下运行。

引用https://www.cnblogs.com/f-ck-need-u/p/7446194.html

原文地址:https://www.cnblogs.com/tingxin/p/12210716.html

时间: 2024-10-16 17:15:40

子shell的相关文章

SHELL脚本攻略(读书笔记)--1.11 命令替换和子shell的作用

1.11.1 命令替换 Linux中使用反引号"``"(在波浪线的按键上)或者$()来执行命令替换.一般以$()更直观也更方便敲入. [[email protected] tmp]# echo Can you tell me what date it is?  Oh my pleasure $(date +%F)                    Can you tell me what date it is? Oh my pleasure 2016-09-25 [[email p

批量kill java进程方法-引出子shell和反引用

方法: kill –9 `pgrep java` 使用上述命令可以将服务器上运行的所有java进程一次性kill掉. 扩展:子shell和反应用在shell脚本中的作用 先来看一个子shell的例子: # cat text1.txt 1 2 3 4 5 # text01=$(cat text1.txt) #echo $text01 1 2 3 4 5 从例子可以看出,子shell是用$()引用起来的部分,子shell运行作为一个单独的进程,并不会父shell产生影响.子shell输出默认不保留换

老男孩新书Shell编程实战预定开始了-包邮包答疑

老男孩新书Shell编程实战预定开始了(扫图中二维码购买包邮定期包答疑),亲爱的伙伴们,让你们久等了.电脑用户猛戳 电脑购买链接 注意:包邮包3个月(2017春节后起算)Q群书籍内容答疑,等于白送书,还倒搭答疑服务. 如果觉得给力,请帮忙朋友圈.QQ群.空间转载! 活动优惠仅限本次活动,从其他渠道购买不享受此次的服务承诺. 定价 89元 基本信息 作者: 老男孩 丛书名: Linux/Unix技术丛书 出版社: 机械工业出版社 ISBN:9787111556077 上架时间:2017-1-6 出

linux基础学习-第九天(shell基础)

2016-08-10 授课内容: shell脚本基础: 变量 运算 bash测试(数字测试.字符测试.文件测试.组合测试) read命令 变量作用: 1.数据存储格式 2.参与的运算 3.表示的数据范围 变量类型: 1.本地变量 2.环境(全局)变量 3.特殊变量 驼峰命名变量:每个单词一个字母大写 本地变量:生效范围为当前shell进程:对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效 配置文件:~./bashrc ~/.bash_profile 变量赋值:

Linux操作系统基础解析之(七)——Bash(Shell)基础知识(2)

三.命令历史bash从Korn Shell和C Shell中吸收了很多的精华,其中之一正式为已经执行过的命令保存一个缓存副本的特性,我们称之为"命令历史"功能.我们为什么要使用命令历史功能呢?每个用户登录成功之后,尤其是使用bash这样的文件接口登录之后,所有的操作都是由执行命令来实现的,那么就不可避免的会出现重复执行某个命令的情况,如果每个命令都依靠键入的方式来输入的话,固然没有问题,但是效率不高而且也是浪费时间的"可耻"行为,命令历史刚好给我们提供了解决这种问题

Shell 编程基础 --语法快速入门

简单的说shell就是一个包含若干行Shell或者Linux命令的文件.对于一次编写,多次使用的大量命令,就可以使用单独的文件保存下来,以便日后使用.通常shell脚本以.sh为后缀,第一行一定要指明系统需要哪种shell解释用户的shell程序,如:#!/bin/bash .#!/bin/sh等.在这里我们使用#!/bin/bash. 通常shell由Linux命令(外部命令).Shell(内部命令),控制语句和注释语句等组成,类似与windows下的批处理文件(.bat).还需要注意的是我们

Shell脚本编程知识点总结及范例

 一:关于语言 1)编译性语言 编译型语言多半运作于底层,所处理的是字节.整数.浮点数或其它及其机器层经的对象.处理过程为:源程序--预处理--编译--汇编--链接,编译性语言为静态语言. 2)解释性语言 解释性语言读入程序代码并将其转化为内部的形式加以执行.处理过程:解释性(文本文件)-解释器去读取并执行.解释性语言为动态语言. 二:基础 变量类型 linux脚本中的变量不需要事先声明,而是直接定义使用(这点不同于其他高级编程语言中变量的使用)bash变量类型分为本地变量和环境变量. 本地变量

Linux之进程管理及Shell脚本

使用!来调用过往命令 !! 重复执行上一条指令 !a 重复执行上一条以a为首的指令 !nubmer 重复执行上一条在history表中记录号码为number的指令 !-number重复执行前第number条指令 ailas abc='cd xxxxxxxxxxx/xxxxxxxx/xxxxxxxxxxxxx' unalias abc " "将一串字符当成字符串来看,可以转译特殊字符 ' '将一串字符当成字符串来看,无法转译特殊字符 ·能够返回命令执行的结果 echo `uname -a

学习shell script

如果你的想要管理好你的主机,那么就要好好学习自动管理系统的有效工具--hell script!基本上,shell script有点像早期的批处理文件,即将很多命令整合起来一次执行,但是shell script拥有更强大的功能,它可以进行类似程序的编写,并且不需要经过编译就能够执行,非常方便我们对系统进行管理. 今天我们主要通过对一些shell script的习题来加深对脚本知识的学习. 习题1:对成绩进行判断,要求输入一个正整数,以60分,85分为界输出不同的评语. #!/bin/bash #