Appearance
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 之间的内容作为输入传给 cat,cat 重定向到文件。EOF 是结束标记,习惯用 EOF 或 END,写什么字都行,只要前后一致。
<<'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 边存边显示。这些组合起来,脚本的输入输出控制就很灵活了。
下一篇用前面学的所有文本处理工具,做几个真实的日志分析实战。