Skip to content

13|重定向进阶:进程替换与脚本组合

Linux 基础系列讲过重定向基础——> 输出到文件、>> 追加、2>&1 合并错误、| 管道。本篇讲在 Shell 脚本里更进阶的用法:heredoc、进程替换、重定向和函数循环的组合。

这些是写复杂脚本时用得上的——生成配置文件、对比两个命令的输出、把循环的输出统一重定向。

一、heredoc:写多行内容

脚本里要生成一段多行配置文件,用 echo 一行行写很啰嗦。heredoc 一次写多行:

bash
cat > /etc/myapp/config.conf <<'EOF'
[server]
port=8080
host=0.0.0.0

[log]
level=info
path=/var/log/myapp
EOF

<<'EOF'EOF 之间的内容作为输入传给 catcat 重定向到文件。EOF 是结束标记,习惯用 EOFEND,写什么字都行,只要前后一致。

<<'EOF'(带引号)里不展开变量,原样输出:

bash
cat > greet.txt <<'EOF'
你好 $USER
EOF
# 文件内容:你好 $USER($USER 没展开)

<<EOF(不带引号)里展开变量和命令

bash
cat > info.txt <<EOF
主机: $(hostname)
时间: $(date)
EOF
# 文件内容:主机: web-01 / 时间: 2026-06-23 ...

要嵌变量用不带引号的 heredoc,要原样输出(写脚本模板、配置里含 $ 符号)用带引号的。

二、进程替换 <()

命令想以"文件"为参数,但数据来自另一个命令的输出。传统做法要先存临时文件:

bash
# 老办法:存临时文件
command1 > /tmp/a.txt
command2 > /tmp/b.txt
diff /tmp/a.txt /tmp/b.txt
rm /tmp/a.txt /tmp/b.txt

进程替换 <(命令) 把命令输出当成临时文件喂给另一个命令,省去临时文件:

bash
diff <(command1) <(command2)

实战——对比两台机器的文件:

bash
diff <(ssh web-01 cat /etc/nginx/nginx.conf) <(ssh web-02 cat /etc/nginx/nginx.conf)

对比本地和远程:

bash
diff local.conf <(ssh web-01 cat /etc/nginx/nginx.conf)

<() 让命令输出能当文件用,在对比、合并场景特别有用。

还有 >() 输出方向的进程替换,用得少:

bash
# 把日志同时存文件和显示
some_command > >(tee logfile) 2>&1

三、重定向和循环

循环整体重定向——把循环里所有 echo 的输出一次性写进文件:

bash
# 循环外重定向,结果写进文件
for host in web-01 web-02 db-01; do
    echo "$host $(ssh $host uptime)"
done > report.txt

cat report.txt
# web-01 up 47 days...
# web-02 up 12 days...
# db-01 up 3 days...

done > report.txt 把整个循环的输出重定向到文件,循环里不用每行写 >>

循环里读文件的标准写法(上一篇循环篇提过):

bash
while IFS= read -r line; do
    echo "$line"
done < input.txt

< input.txt 把文件作为 while 的输入。

四、重定向和函数

函数内部也能重定向。把日志重定向封装进函数:

bash
run_with_log() {
    "$@" > /var/log/myapp.log 2>&1
}

run_with_log nginx -t

"$@" 把函数参数当命令执行,> log 2>&1 把命令的 stdout 和 stderr 都写进日志。

函数调用时也能重定向——给函数加重定向等于给函数内所有命令加:

bash
collect_info() {
    hostname
    uptime
    df -h
}

collect_info > system-info.txt    # 函数所有输出进文件

五、exec 重定向

exec 在脚本里改变整个脚本的重定向,不用每条命令都写:

bash
#!/usr/bin/env bash
exec > /var/log/myscript.log 2>&1

echo "脚本开始"
# 后面所有 echo 和命令输出都进日志文件

exec > file 后,脚本后续所有输出都重定向到 file。常用来让脚本的所有日志集中到一个文件。

小心:exec 改了重定向就改不回来(除非再 exec 一次)。调试时可能想注释掉这行看屏幕输出。

六、文件描述符

0 1 2 是标准输入、输出、错误。还可以用 3-9 自定义文件描述符:

bash
# 打开一个文件作为 fd 3
exec 3> /tmp/debug.log

# 往 fd 3 写
echo "调试信息" >&3

# 关闭 fd 3
exec 3>&-

用得少,但在需要"同时往多个地方写"或"暂存某路输出"时有用。

七、tee:同时输出和保存

> 把输出写进文件,屏幕就看不到了。tee 同时做两件事——存文件又显示:

bash
some_command | tee output.txt

调试脚本时 tee 很有用——既看得到输出又留了记录。tee -a 追加不覆盖:

bash
echo "运行于 $(date)" | tee -a run.log

八、实际组合

一个巡检脚本片段,把重定向组合起来用:

bash
#!/usr/bin/env bash
set -euo pipefail

report="/tmp/report.$(date +%s).txt"

{
    echo "=== 巡检报告 ==="
    echo "时间: $(date)"
    echo

    echo "--- 主机信息 ---"
    hostname
    uptime

    echo "--- 磁盘 ---"
    df -h

    echo "--- 内存 ---"
    free -h

    echo "--- 异常服务 ---"
    systemctl --failed --no-pager || true
} > "$report"

echo "报告已生成: $report"

{ ... } 把一组命令的输出合并重定向到一个文件,不用每条命令都写 >>。这是生成报告的常用骨架。

学会了之后

heredoc 写多行配置、进程替换让命令输出当文件用、循环和函数整体重定向、exec 改全局重定向、tee 边存边显示。这些组合起来,脚本的输入输出控制就很灵活了。

下一篇用前面学的所有文本处理工具,做几个真实的日志分析实战。