shell学习系列之父shell、子shell、脚本执行的几种方式

参考文章:
1)https://blog.csdn.net/NOStandby/article/details/82914930?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
2)https://blog.csdn.net/taiyang1987912/article/details/39529291
3)https://blog.csdn.net/dreamcoding/article/details/8519689?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase


一、简介

Linux是一种用户控制的多作业操作系统,系统允许多个系统用户同时提交作业,而一个系统用户又可能用多个shell登录,每个系统用户可以用一个shell提交多个作业。了解Bash Shell在多作业管理和进程处理方面的命名和机制有助于理解多用户、多作业的系统。

二、详解

1、子Shell

(1)父子Shell是相对的,它描述了两个Shell进程的fork关系,父Shell指在控制终端或xterm窗口给出提示符的进程,子Shell是由父Shell创建的进程。父Shell创建子Shell调用的是fork函数。

Shell命令可以分为内建命令(Shell本身执行的命令)和外部命令(fork创建出来的子shell执行的命名),内建命令不创建子Shell而外部命令创建子Shell。

(2) 内建命令是包含在Shell工具包中的命令,其中保留字对Shell有特殊含义,保留字本身不是一个命令而是命令结构的一部分。

冒号是Shell中一个特殊的符号,首先冒号可以表示永真(相当于TRUE关键字)如while :;do…done(while循环的条件始终为真);其次冒号可以清空一个文件,:>log将冒号重定向到文件,log文件内容被清空,所以:>命名是常用的清空文件的命令;接着冒号最重要的用法是:不做任何事,只做参数展开。

(3)圆括号结构,能强制将其中的命令运行在子shell中,bash3后定义了内部变量BASH_SUBSHELL记录子shell的层次。
BASH_SUBSHELL 是一个环境变量,它表示进入子 shell 的层级,比如处于当前 shell 时,该变量值为 0;当在当前 shell 派生的子 shell 里时,该变量值为 1;如果该子 shell 又派生出一个子 shell,那么该变量在此间的值就为 3,以此类推。

2、export命令

说到父shell 和 子 shell,那么会想到 export 这个命令。export 也是 bash 的一个内置命令。它主要是用来将父 shell 里的变量导出供子 shell 使用。它有如下特征:1. 用 export 导出的变量放在“导出变量列表”中,它可以被子 shell (子 shell 的子 shell 也是如此)拷贝并使用。2. 被 export 出来的变量虽然可以被子 shell 使用,但它也只是一个拷贝,而不会影响到父 shell 中的值以及其它子 shell 中的值。
(1)在当前 shell 里 export 一个变量。
(2)父 shell 里 export 的变量可以被子 shell 读取。
(3)子 shell 对父 shell 里 export 出来的变量进行修改并不能影响到父 shell。这说明了,子 shell 只是在“导出变量列表“里对该变量进行了一个拷贝。但反过来,父shell再次更改此变量时,子 shell 再去读时,读到的是新值,而不是原来的值。
(4)export 出来的变量不能导出到父进程或者是父进程的环境里。一个子进程可以继承父进程的东西,而不能反过来去影响父进程。

3、子 shell 向父 shell 传递自己的变量

下面方法可以考虑:

通过一个中间文件进行:

#!/bin/bash

(
 subvar="hello shell"
 echo "$subvar" > temp.txt
)

read pvar < temp.txt

echo $pvar

运行输出:

$ sh subandp.sh
hello shell

通过命令替换:

#!/bin/bash

pvar=`subvar="hello shell";echo $subvar`

echo $pvar

运行输出: ::

$ ./subandp.sh
hello shell

执行命令替换符(两个反单引号)之间的命令也是在子 shell 来完成的。

使用命名管道:

#!/bin/bash

mkfifo -m 777 npipe

(
  subsend="hello world"
  echo "$subsend" > npipe &
 )

read pread < npipe

echo "$pread"

exit 0

运行输出:

beyes@debian:~/shell$ ./var.sh
hello world

关于有名管道创建命令 mkfifo 可参考:http://www.groad.net/bbs/read.php?tid-3707.html

使用 here 文档

#!/bin/bash

read pvar << HERE
`subvar="hello shell"
echo $subvar`
HERE

echo $pvar

运行输出:

$ ./subandp.sh
hello shell

方法应该还有很多,这些方法的本质原理基于进程间的通信。

三、脚本执行的几种方式

Linux执行Scripts有两种方式,主要区别在于是否建立subshell

1. source filename or . filename

不创建subshell,在当前shell环境下读取并执行filename中的命令,相当于顺序执行filename里面的命令

2. bash filename or ./filename

创建subshell,在当前bash环境下再新建一个子shell执行filename中的命令。
子shell继承父shell的变量,但子shell不能使用父shell的变量,除非使用export
【备注:这和命名空间是相似的道理,甚至和c中的函数也有些类似】

子Shell从父Shell继承得来的属性如下:

  • 当前工作目录
  • 环境变量
  • 标准输入、标准输出和标准错误输出
  • 所有已打开的文件标识符
  • 忽略的信号

子Shell不能从父Shell继承的属性,归纳如下:

  • 除环境变量和.bashrc文件中定义变量之外的Shell变量
  • 未被忽略的信号处理

3. $ (commond)

它的作用是让命令在子shell中执行

4. `commond`

和$(commond)差不多。
【这里的“ ` ”符号是撇(反单引号),不是单引号,是键盘上Esc按键下面的那个键。】

5. exec commond

替换当前的shell却没有创建一个新的进程。进程的pid保持不变
作用:
shell的内建命令exec将并不启动新的shell,而是用要被执行命令替换当前的shell进程,并且将老进程的环境清理掉,而且exec命令后的其它命令将不再执行。
当在一个shell里面执行exec ls后,会列出了当前目录,然后这个shell就自己退出了。(后续命令不再执行)
因为这个shell已被替换为仅执行ls命令的进程,执行结束自然也就退出了。
需要的时候可以用sub shell 避免这个影响,一般将exec命令放到一个shell脚本里面,用主脚本调用这个脚本,调用点处可以用bash a.sh(a.sh就是存放该命令的脚本),这样会为a.sh建立一个sub shell去执行,当执行到exec后,该子脚本进程就被替换成了相应的exec的命令。

猜你喜欢

转载自blog.csdn.net/daisy2001daisy/article/details/106316397