Skip to content

Subshell

Subshell(子 Shell)是在当前 Shell 进程中创建的一个子进程,继承父 Shell 的环境但在独立的空间中运行。

什么是 Subshell

bash
# 使用括号创建 subshell
(
    echo "子 shell 中的 PID:$BASHPID"
    cd /tmp
    VAR="在子 shell 中"
)

echo "父 shell 中的 PID:$BASHPID"
echo "当前目录:$(pwd)"   # 仍在原目录,cd 不影响父 shell
echo "VAR:$VAR"          # 空,子 shell 的变量不影响父 shell

创建 Subshell 的方式

1. 括号 ( )

bash
(命令1; 命令2; 命令3)

# 示例
(cd /tmp; ls; pwd)
echo "父 shell 目录:$(pwd)"   # 不变

2. 命令替换 $( )

bash
output=$(cd /tmp && ls)
echo "$output"

3. 管道

bash
# 管道两侧的命令都在 subshell 中运行
echo "hello" | read line   # 不推荐,read 在 subshell 中,变量丢失
echo "hello" | { read line; echo "读取:$line"; }  # 同一 subshell

4. 进程替换 <( )>( )

bash
# 将命令输出作为文件输入
diff <(ls dir1) <(ls dir2)

# 将输出重定向到命令
tee >(gzip > output.gz) > output.txt

Subshell 的特性

继承父 Shell 的变量

bash
PARENT_VAR="来自父 shell"

(
    echo "$PARENT_VAR"   # 可以读取父 shell 变量
    PARENT_VAR="在子 shell 中修改"
    echo "$PARENT_VAR"   # 显示修改后的值
)

echo "$PARENT_VAR"   # 仍是"来自父 shell",子 shell 修改不影响父 shell

继承函数和别名

bash
greet() { echo "Hello, $1!"; }

(
    greet "World"   # 可以调用父 shell 定义的函数
)

独立的工作目录

bash
(cd /tmp; pwd)   # 输出 /tmp
pwd              # 输出原目录

Subshell 的用途

临时改变环境

bash
#!/bin/bash

# 在子 shell 中设置不同的 IFS,不影响外部
(
    IFS=:
    for part in $PATH; do
        echo "$part"
    done
)
echo "IFS 恢复了:'$IFS'"

隔离错误

bash
#!/bin/bash

# 子 shell 中的 exit 不会终止父 shell
(
    echo "进入子 shell"
    exit 1
    echo "这行不会执行"
)
echo "父 shell 继续运行,子 shell 退出码:$?"

并行执行

bash
#!/bin/bash

# 在 subshell 中并行执行任务
(sleep 2; echo "任务 1 完成") &
(sleep 1; echo "任务 2 完成") &
(sleep 3; echo "任务 3 完成") &

wait
echo "所有任务完成"

分组重定向

bash
#!/bin/bash

# 将多个命令的输出重定向到同一个文件
(
    echo "=== 系统信息 ==="
    uname -a
    echo "=== 磁盘使用 ==="
    df -h
    echo "=== 内存使用 ==="
    free -h
) > system_report.txt

Subshell vs 函数

特性Subshell ( )函数
变量隔离否(除非 local)
目录隔离
exit 行为只退出子 shell退出整个脚本
性能较低(fork 进程)较高
使用场景需要隔离时代码复用

$BASH_SUBSHELL 变量

bash
echo "父 shell 层级:$BASH_SUBSHELL"   # 0

(
    echo "第 1 层 subshell:$BASH_SUBSHELL"   # 1
    (
        echo "第 2 层 subshell:$BASH_SUBSHELL"   # 2
    )
)

注意事项

bash
#!/bin/bash

# 管道中的变量问题
count=0
cat file.txt | while read line; do
    (( count++ ))   # count 在 subshell 中修改,外部看不到
done
echo "count: $count"   # 0!

# 解决方案:使用进程替换
count=0
while read line; do
    (( count++ ))
done < <(cat file.txt)
echo "count: $count"   # 正确的行数