LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 223|回复: 0

Shell脚本深入教程:Shell环境和子Shell的概念

[复制链接]
发表于 2023-12-14 14:16:12 | 显示全部楼层 |阅读模式
Shell环境和子Shell
shell如何让命令执行
假设敲下命令行:

echo hello
shell是如何让echo命令执行起来的?

shell首先读取命令行、解析命令行,解析期间会发现echo是一个外部命令。解析通过后,那么就准备让echo命令执行起来。



因为exec是调用echo程序来替换子shell进程,所以子shell进程继承自父shell的属性都会被覆盖,也就是说,继承自父Shell的变量已经不存在了。



  1. +--------+
  2. | pid=7  |
  3. | ppid=4 |
  4. | bash   |
  5. +--------+
  6.     |
  7.     | calls fork
  8.     V
  9. +--------+             +--------+
  10. | pid=7  |    forks    | pid=22 |
  11. | ppid=4 | ----------> | ppid=7 |
  12. | bash   |             | bash   |
  13. +--------+             +--------+
  14.     |                      |
  15.     | waits for pid 22     | calls exec to run echo
  16.     |                      V
  17.     |                  +--------+
  18.     |                  | pid=22 |
  19.     |                  | ppid=7 |
  20.     |                  | echo   |
  21.     V                  +--------+
  22. +--------+                 |
  23. | pid=7  |                 | exits
  24. | ppid=4 | <---------------+
  25. | bash   |
  26. +--------+
  27.     |
  28.     | continues
  29.     V
复制代码

上面是Shell如何让外部命令运行的过程。但在Shell中,可执行的内容有多种。

Shell中的可执行程序
外部命令:比如cat,ls
shell内置命令:比如cd,set,export,read
shell函数
别名
shell保留关键字:time,while,if,case,do,done,function,for等
Shell让它们执行的方式不一样:

外部命令的执行需要先fork子shell进程,然后在子shell进程中exec调用外部命令
shell函数、shell内置命令、shell保留关键字都依赖于Shell进程,没有Shell进程,它们都没有意义。它们都是直接在当前Shell进程内执行的,不会创建新的子Shell进程来执行
别名会在命令行解析阶段先替换成对应的内容,然后重新执行命令行解析

  1. 补充:命令优先级

  2. 当别名、shell保留关键字、shell函数、shell内置命令、外部命令的名称有冲突时,会执行谁呢?详细内容可参考:[url=https://www.junmajinlong.com/shell/call_order/]https://www.junmajinlong.com/shell/call_order/[/url]

  3. 这里只给它们的优先级:别名>保留关键字>shell函数>shell内置命令>外部命令
复制代码

Shell环境
何为环境?在Shell中这是极其重要的概念,是深入Shell必须搞懂的内容,所以请一定要重视。

每个shell进程有一个自己的运行环境,不同的Shell进程有不同的Shell环境。Shell解析命令行、调用命令行的过程都在这个环境中完成。

可以把一个Shell环境想象成一个盒子。

关于环境的几个结论:

调用shell程序(比如bash)时,会读取shell配置文件来初始化Shell环境。对于bash来说,是读取:
/etc/profile
/etc/profile.d/*.sh
~/.bash_profile
~/.bashrc
/etc/bashrc
环境主要体现在对环境的设置,包括但不限于的环境设置有:
cd /tmp表示设置当前shell环境的工作目录(cd命令是一个内置命令)
shopt或set命令进行的shell功能设置,即打开或关闭某个功能
比如shopt -s globstar表示开启当前Shell环境的双星号**目录递归通配功能,例如grep "declare" /etc/**/*.sh
环境变量设置
主要用于Shell进程和其子进程之间的数据传递
子进程(不仅仅是子Shell进程)可以继承父Shell环境中的环境变量
环境变量通常以大写字母定义,但非一定
使用bash内置命令export可以定义环境变量
命令前定义变量var=value cmd,表示定义一个专属环境变量,该环境变量只能在cmd进程环境中可访问,其它任何地方都无法访问,cmd进程退出后,var环境变量也消失
export X=xyz表示在当前Shell环境下定义一个环境变量X,以便让子进程继承这个变量
每当提到shell内置命令,就要想到这个命令的作用有可能是在当前Shell环境下进行某项设置
shell内置命令不会创建新进程,而是直接在当前Shell环境内部执行
内置命令source或.执行脚本时,表示在当前Shell环境下执行脚本内容,即脚本中的所有设置操作都会直接在当前Shell下设置
父Shell环境可能会影响子Shell环境,但子Shell环境一定不影响父Shell环境,比如Shell脚本中的环境变量不会粘滞到父Shell环境中
何时会进入子Shell:两种子Shell
如何是不同的Shell环境?查看$BASHPID的值即可,如果变量的值不同,说明进入了新的Shell环境。

echo $BASHPID
有两种类型的子Shell:

(1).当前Shell进程因命令行中使用了某些特殊语法而产生的子Shell:
小括号(cmd1;cmd2)
管道cmd1|cmd2
命令替换$(cmd)
进程替换<(cmd),>(cmd)
(2).显式调用bash程序产生的子Shell进程:
bash -c 'cmd'
bash命令
shell脚本
第一种进入子Shell的方式,在fork产生子bash进程后不会执行exec,所以会继承父Shell很多设置,比如继承普通变量。

第二种进入子Shell的方式,在刚fork产生子bash进程时也会继承父Shell设置,但因为exec调用bash程序会替换覆盖掉子bash进程,所以这种方式只会保留环境变量。

第二种方式的子Shell环境更应该称为一个独立的进程环境,只不过这个进程是Shell进程,且和父Shell有父子进程的关系,所以也可以称为子Shell。

另外需要注意,shell内置命令、shell函数、shell保留关键字都不会主动开启子Shell,但如果将它们放在管道前后,由于它们依赖于shell进程,所以会单独创建一个新的子shell进程提供它们的运行环境。

比如,Shell环境1中的设置、变量,在第一种形式的子Shell环境2中都生效,在第二种形式的子Shell环境3中都不生效。

  1. $ echo $BASHPID
  2. $ x=10
  3. $ echo $x
  4. $ shopt -s globstar
  5. $ shopt globstar
  6. globstar        on

  7. # 进入第一种形式的子Shell环境
  8. $ (echo $x)         # 10
  9. $ (shopt globstar)
  10. globstar        on

  11. # 进入第二种形式的子Shell环境
  12. $ bash
  13. $ echo $x
  14. $ shopt globstar
  15. globstar        off
复制代码

文章链接: https://www.junmajinlong.com/shell/script_course/shell_env/
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表