Shell环境和子Shell
shell如何让命令执行
假设敲下命令行:
echo hello
shell是如何让echo命令执行起来的?
shell首先读取命令行、解析命令行,解析期间会发现echo是一个外部命令。解析通过后,那么就准备让echo命令执行起来。
因为exec是调用echo程序来替换子shell进程,所以子shell进程继承自父shell的属性都会被覆盖,也就是说,继承自父Shell的变量已经不存在了。
- +--------+
- | pid=7 |
- | ppid=4 |
- | bash |
- +--------+
- |
- | calls fork
- V
- +--------+ +--------+
- | pid=7 | forks | pid=22 |
- | ppid=4 | ----------> | ppid=7 |
- | bash | | bash |
- +--------+ +--------+
- | |
- | waits for pid 22 | calls exec to run echo
- | V
- | +--------+
- | | pid=22 |
- | | ppid=7 |
- | | echo |
- V +--------+
- +--------+ |
- | pid=7 | | exits
- | ppid=4 | <---------------+
- | bash |
- +--------+
- |
- | continues
- 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进程来执行
别名会在命令行解析阶段先替换成对应的内容,然后重新执行命令行解析
- 补充:命令优先级
- 当别名、shell保留关键字、shell函数、shell内置命令、外部命令的名称有冲突时,会执行谁呢?详细内容可参考:[url=https://www.junmajinlong.com/shell/call_order/]https://www.junmajinlong.com/shell/call_order/[/url]
- 这里只给它们的优先级:别名>保留关键字>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中都不生效。
- $ echo $BASHPID
- $ x=10
- $ echo $x
- $ shopt -s globstar
- $ shopt globstar
- globstar on
- # 进入第一种形式的子Shell环境
- $ (echo $x) # 10
- $ (shopt globstar)
- globstar on
- # 进入第二种形式的子Shell环境
- $ bash
- $ echo $x
- $ shopt globstar
- globstar off
复制代码
文章链接: https://www.junmajinlong.com/shell/script_course/shell_env/
|