Linux中父shell 与 子shell :subshell 和 child shell

子shell的概念

中文翻译过来的“子shell”叫法,实际上包括了“subshell”和“child shell”。

subshell是指: 对父shell的一个拷贝,共享了父shell的变量,函数,别名等,能用但修改只在subshell中生效。通过()开启subshell。内置变量BASH_SUBSHELL显示subshell的层数。

child shell是指: 重新开启的一个shell,不共享父shell的变量,函数,别名。一个纯新的环境。命令bash或者运行脚本就开启child shell。内置变量SHLVL显示child shell的层数。

The Korn Shell: Unix & Linux Programming Manual :
A subshell is a separate copy of the parent shell, so variables, functions, and aliases from the parent shell are available to the subshell

# BASH_SUBSHELL
> echo $BASH_SUBSHELL
0
> (echo $BASH_SUBSHELL)
1  --进入1层 subshell
> ( ( ( ( (echo $BASH_SUBSHELL) ) ) ) )
5  --进入5层 subshell
> bash 
> echo $SHLVL
1 --进入1层 child shell
> echo $BASH_SUBSHELL
0
> bash 
> echo $SHLVL
2 --进入2层 child shell
> echo $BASH_SUBSHELL
0

一、进入子shell的方式

  1. 直接输入bash(退出 :exit,也无法继承和改变父shell中的普通变量,同脚本):child shell
  2. ()中的命令是在子shell中实现:subshell
  3. 运行脚本,脚本也是在子shell中实现 :child shell

二、差异

child shell是一个全新的环境,不共享父shell中的普通变量,普通变量只能通过export方式实现共享。

subshell 中的普通变量是可以从父shell中继承的,只是不能改变父shell中的变量值。

  • 验证环境:
> cat /proc/version 
Linux version 3.0.101-0.47.55-default (geeko@buildhost) (gcc version 4.3.4 [gcc-4_3-branch revision 152973] (SUSE Linux) ) #1 SMP Thu May 28 08:25:11 UTC 2015 (dc083ee)
  • ()的表现:
> x=10
> echo $x
10
> echo $$ #该命令用于显示当前shell进程号
10497  --父shell的进程号
> (echo $$;echo $x;ps;x=5;echo $x)
10497 -- 实际上还是父shell的进程
10  -- 进程中的x可取父进程中的x
5   -- 进程中的x被改变
  PID TTY          TIME CMD
 3757 pts/0    00:00:00 bash #subshell进程进程号
 3758 pts/0    00:00:00 ps
10497 pts/0    00:00:00 bash #父进程进程号
> echo $x
10   -- 父进程中的x没有改变
  • bash命令
> bash #进入子shell
> echo $$
3609
> ps
  PID TTY          TIME CMD
 3609 pts/0    00:00:00 bash --child shell进程
 5359 pts/0    00:00:00 ps
10497 pts/0    00:00:00 bash
> echo $x
   -- 空
> x=5;echo $x
5 
> exit #退出子进程
> echo $$ 
10497 --回到父shell
> echo $x
10 -- 父shell中x没有改变
  • 脚本中子shell的表现:
    echo $$;echo $x;ps;x=5;echo $x写入test.sh中
> echo $x
10 
> ./test.sh 
 8794 --子shell的进程号
          -- 无法获取父shell中的变量值
  PID TTY          TIME CMD
 8794 pts/0    00:00:00 sh -- child shell 进程
 8795 pts/0    00:00:00 ps
10497 pts/0    00:00:00 bash
5 -- 子shell中定义的变量

> echo $x
10 --父shell中值不变

三、差异的原因

()的子shell能获取父shell中变量值的原因因为是subshell,$()一样能够获取到父shell中变量的值。$()命令的()表现和单独的()一样,也是在子shell中运行,但能访问父shell变量,但不能改变父shell变量。

参考:
https://stackoverflow.com/questions/19462291/bash-subshell-parentheses-vs-dollar-parentheses
https://tldp.org/LDP/abs/html/subshells.html

https://www.cnblogs.com/ziyunfei/p/4803832.html

<think>我们正在讨论Linux shell脚本中`wait`命令的行为,特别是当在函数中调用`wait`时,它可能会阻塞函数中的后台进程的问题。 根据用户的问题,用户遇到了在函数中调用`wait`导致上级函数中的后台程序被阻塞的情况。我们需要解释这种行为的原因并提供解决方案。 ### 问题分析 在shell脚本中,`wait`命令用于等待所有后台进程完成或等待指定的后台进程完成。但是,`wait`的作用范围是当前shell环境中的所有后台进程,包括当前函数及其函数中启动的后台进程。 因此,如果在函数中调用了`wait`(不带任何参数),那么它会等待当前shell中所有未完成的后台进程,包括在函数中启动的后台进程。这就会导致函数中的后台进程被阻塞,直到函数中的`wait`完成。 ### 示例重现问题 考虑以下脚本: ```bash #!/bin/bash parent_function() { echo "Parent: starting background job" sleep 5 & pid=$! echo "Parent: background job PID: $pid" child_function # 函数中等待后台进程 wait $pid echo "Parent: background job completed" } child_function() { echo "Child: starting background job in child" sleep 10 & echo "Child: waiting for all background jobs in child function" wait # 这里会等待所有后台进程,包括函数的sleep 5 echo "Child: wait done" } parent_function ``` 在这个脚本中: 1. 在`parent_function`中启动了一个`sleep 5`的后台进程。 2. 然后调用`child_function`。 3. 在`child_function`中启动了一个`sleep 10`的后台进程,然后调用`wait`(不带参数)等待所有后台进程完成。 4. 由于`wait`会等待所有后台进程,所以它也会等待函数中的`sleep 5`(尽管它只需要5秒,但这里由于函数中的`sleep 10`需要10秒,所以`wait`会等待10秒)。 5. 当函数中的`wait`返回后,函数继续执行,然后函数中执行`wait $pid`等待特定的进程(这个进程在函数中已经等待过了,所以会立即返回)。 结果:函数中的后台进程(5秒)实际上被阻塞了10秒(因为函数中的`wait`等待了10秒)。 ### 解决方案 要避免函数中的`wait`影响函数中的后台进程,我们可以使用以下方法: #### 方法1:在函数中只等待函数启动的后台进程 在函数中,我们只等待在该函数中启动的后台进程。我们可以通过记录函数中启动的后台进程的PID,然后使用`wait`指定这些PID。 修改后的函数: ```bash child_function() { echo "Child: starting background job in child" sleep 10 & local child_pid=$! echo "Child: background job PID: $child_pid" echo "Child: waiting for child background jobs only" wait $child_pid echo "Child: wait done" } ``` 这样,函数中的`wait`只会等待函数自己的后台进程,而不会影响函数的后台进程。 #### 方法2:使用shell函数中,如果使用shell来执行后台进程,那么这些后台进程将属于shell,而shell的`wait`只会等待它自己启动的后台进程。但是注意,在函数内部使用shell可能会限制对变量的修改(因为shell有自己的环境),所以需要根据情况使用。 例如: ```bash child_function() { ( echo "Child: starting background job in child (subshell)" sleep 10 & local child_pid=$! wait $child_pid echo "Child: background job in subshell completed" ) } ``` 在这个版本中,函数使用了一个shell(由括号包围),在shell中启动后台进程并等待。这样,函数中的后台进程不会被shell中的`wait`影响。 ### 总结 在shell脚本中,`wait`命令默认会等待当前shell中的所有后台进程。如果在函数中调用无参数的`wait`,它会等待包括函数启动的后台进程在内的所有后台进程,从而导致阻塞。为了避免这种情况,我们应该在函数中明确指定要等待的进程PID(即只等待函数自己启动的后台进程),或者将函数中的后台进程放在shell中执行。 ### 相关问题 1. 在shell脚本中,如何获取后台进程的PID? 2. 除了使用`wait`,还有哪些方法可以同步后台进程? 3. 在shell脚本中,shell函数调用有什么区别?</think>### Linux Shell脚本中`wait`命令在函数中的阻塞问题分析 在Linux shell脚本中,`wait`命令用于等待后台进程完成。当在函数中调用`wait`时,可能会导致上级函数中的后台进程被阻塞,这是因为`wait`的作用范围是当前shell环境中的所有后台进程。 #### 问题重现 考虑以下示例脚本: ```bash #!/bin/bash parent_func() { echo "函数启动后台任务" sleep 5 & # 后台任务1 local parent_pid=$! child_func # 函数等待自己的后台任务 wait $parent_pid echo "函数后台任务完成" } child_func() { echo "函数启动后台任务" sleep 10 & # 后台任务2 echo "函数调用wait" wait # 这里会阻塞函数的所有后台任务 echo "函数wait完成" } parent_func ``` 执行结果: ``` 函数启动后台任务 函数启动后台任务 函数调用wait # 这里会阻塞10秒(函数的sleep时间) 函数wait完成 函数后台任务完成 ``` #### 问题原因 1. **`wait`的作用范围** 当不带参数调用`wait`时,它会等待**当前shell环境中的所有后台进程**(包括函数启动的进程) 2. **函数继承环境** 函数继承了函数的shell环境,包括后台进程列表 3. **执行顺序** 函数中的`wait`会阻塞脚本执行,直到**所有后台进程完成**(包括函数启动的进程) #### 解决方案 ##### 方法1:明确指定等待的PID(推荐) ```bash child_func() { echo "函数启动后台任务" sleep 10 & local child_pid=$! # 记录函数自己的PID echo "函数等待指定PID" wait $child_pid # 只等待函数的后台进程 echo "函数任务完成" } ``` ##### 方法2:使用shell隔离环境 ```bash child_func() { ( # 在shell中运行 echo "函数启动后台任务" sleep 10 & wait # 只影响shell内的进程 echo "函数任务完成" ) } ``` ##### 方法3:使用进程组控制 ```bash child_func() { echo "函数启动后台任务" sleep 10 & local child_pid=$! # 只等待函数进程组 wait $child_pid echo "函数任务完成" } ``` #### 关键原则 1. **显式指定PID** 始终使用`wait $pid`代替无参数`wait`,精确控制等待对象 2. **记录后台进程PID** 使用`$!`捕获后台进程ID: ```bash sleep 10 & local my_pid=$! ``` 3. **作用域隔离** 在函数中使用shell`(...)`或显式记录PID,避免影响级环境 4. **避免全局等待** 无参数的`wait`会等待所有后台进程,应谨慎使用 ### 相关问题 1. 如何在shell脚本中同时管理多个后台进程的状态? 2. `wait`命令`jobs`命令有什么区别联系? 3. 在shell中运行后台进程会有什么限制? 4. 如何实现shell脚本中多个后台进程的同步控制? 5. 当后台进程异常退出时,`wait`命令会返回什么信息?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值