Shell脚本如何实现输入输出重定向:从基础到高级实战指南
📑 目录导读
- 什么是输入输出重定向? —— 理解核心概念
- 基础重定向操作符 ——
>、<、>>等详解 - 文件描述符与重定向的关系 —— stdin、stdout、stderr
- 标准错误与标准输出的分离与合并 —— 2>&1 与 &>
- Here Document与Here String —— 内联输入重定向
- exec命令的高级重定向技巧 —— 永久改变I/O
- 使用tee命令同时输出到屏幕和文件
- 实战案例:日志收集与错误处理脚本
- 常见问题与QA问答
什么是输入输出重定向?
在Linux/Unix系统中,每个Shell脚本默认从标准输入(stdin)读取数据,并将结果输出到标准输出(stdout),错误信息输出到标准错误(stderr),输入输出重定向允许你改变这些数据流的方向,

- 将命令的输出写入文件(而非屏幕)
- 从文件读取输入(而非键盘)
- 将错误信息单独保存
这种机制是Shell脚本自动化处理数据、日志记录和错误隔离的基石。
基础重定向操作符
| 操作符 | 功能 | 示例 |
|---|---|---|
> |
将stdout覆盖写入文件 | ls > filelist.txt |
>> |
将stdout追加到文件 | echo "new line" >> log.txt |
< |
从文件读取stdin | sort < unsorted.txt |
2> |
将stderr写入文件 | cmd 2> errors.log |
&> |
将stdout和stderr合并写入 | cmd &> all.log |
示例:
# 将当前目录列表写入文件(覆盖) ls -la > directory_output.txt # 错误信息单独保存 cat nonexistent.txt 2> error.log # 从文件读取数据并统计行数 wc -l < data.txt
文件描述符与重定向的关系
Linux系统为每个进程预定义了3个文件描述符(File Descriptor):
- 0:标准输入 (stdin)
- 1:标准输出 (stdout)
- 2:标准错误 (stderr)
重定向本质上是将某个文件描述符指向其他文件或设备。
# 显式指定文件描述符(默认数字1可省略) command 1> output.txt # 等同于 command > output.txt command 2> error.txt # 错误信息重定向
标准错误与标准输出的分离与合并
1 分离输出
# 正常结果存入output,错误存入error find / -name "*.conf" > found.conf 2> search_errors.log
2 合并输出(常见场景)
# 方法一:将stderr重定向到stdout(顺序重要) command > output.txt 2>&1 # 方法二:使用Bash 4+的新语法 command &> output.txt # 方法三:追加合并 command >> output.txt 2>&1
注意: 2>&1 必须放在 > 之后,否则相当于将stderr重定向到之前状态的stdout(通常是终端)。
3 丢弃不想要的输出
# 丢弃stdout command > /dev/null # 丢弃stderr command 2> /dev/null # 丢弃所有输出 command &> /dev/null
Here Document与Here String
1 Here Document(<<)
用于在脚本内嵌入多行输入:
#!/bin/bash cat << EOF > config.ini [server] host = 127.0.0.1 port = 8080 EOF
2 Here String(<<<)
将字符串作为stdin传递给命令:
# 将字符串传递给grep grep "error" <<< "line1 error\nline2 warning\nline3 error"
exec命令的高级重定向技巧
exec 可以在当前Shell中永久改变文件描述符的指向:
#!/bin/bash # 将脚本内所有后续命令的stdout都写入文件 exec > script_output.log 2>&1 echo "This goes to log" ls /nonexistent # 错误也进入日志 echo "Another line"
应用场景: 将所有日志统一记录,避免每个命令手动重定向。
# 打开一个文件作为文件描述符3 exec 3> custom_fd.log echo "Custom FD output" >&3 exec 3>&- # 关闭文件描述符
使用tee命令同时输出到屏幕和文件
tee 命令可以分叉输出,同时显示在终端并写入文件:
# 显示进度同时记录日志 ./long_running_task.sh | tee task_output.log # 追加模式 ./script.sh | tee -a session.log # 同时捕获stderr(需通过重定向合并) ./script.sh 2>&1 | tee combined.log
实战案例:日志收集与错误处理脚本
#!/bin/bash
# 日志收集脚本 - log_collector.sh
# 设置日志文件
LOG_FILE="/var/log/app_$(date +%Y%m%d).log"
ERROR_FILE="/var/log/app_error_$(date +%Y%m%d).log"
# 将整个脚本的输出重定向到日志
exec > >(tee -a "$LOG_FILE") 2> >(tee -a "$ERROR_FILE" >&2)
echo "[INFO] Script started at $(date)"
# 模拟任务
for i in {1..5}; do
echo "[DEBUG] Processing item $i"
if [ $i -eq 3 ]; then
echo "[ERROR] Item $i failed!" >&2
fi
sleep 1
done
echo "[INFO] Script completed"
执行效果:
- 正常信息显示在终端并写入
app_日期.log - 错误信息显示在终端并写入
app_error_日期.log
常见问题与QA问答
❓ Q1: 2>&1 和 &> 有何区别?
A:
2>&1是POSIX标准语法,在任何Shell中都可用(如sh、bash、zsh等),顺序很重要:必须先重定向stdout,再将stderr指向stdout。&>是Bash的简写语法,更直观但非标准,建议在新脚本中使用2>&1以保持兼容性。
❓ Q2: 如何将输出同时重定向到多个文件?
A: 使用 tee 命令或进程替换:
# 方法1: tee command | tee file1 file2 # 方法2: 进程替换(Bash支持) command > >(tee file1) >(tee file2) # 不合理写法 # 正确做法 command | tee file1 | tee file2
❓ Q3: 重定向时文件内容被清空了怎么办?
A: 使用 >> 追加模式而非 > 覆盖模式。
# 错误:覆盖文件 echo "new" > important.txt # 正确:追加内容 echo "new" >> important.txt
❓ Q4: 如何关闭标准输出或标准错误?
A: 重定向到 /dev/null:
# 关闭stdout command > /dev/null # 关闭stderr command 2> /dev/null # 关闭所有输出 command &> /dev/null
❓ Q5: 重定向对管道命令的影响是什么?
A: 管道 只处理stdout,不处理stderr,要同时传递stderr,需要先合并:
# 只处理stdout command1 | command2 # 处理stdout和stderr command1 2>&1 | command2
总结与最佳实践
- 日常日志:使用
>>追加写入,避免丢失历史记录 - 错误处理:始终将stderr单独保存或显式合并
- 调试阶段:使用
tee同时显示输出,不遗漏信息 - 脚本设计:在脚本开头使用
exec统一重定向,简化后续代码 - 安全性:避免将敏感数据(如密码)通过重定向写入可公开访问的文件
掌握输入输出重定向是编写健壮Shell脚本的关键一步,它将简单命令串联为强大的自动化工具,让你的脚本能够优雅地处理数据流、记录运行状态、隔离错误信息,从今天起,在你的每个脚本中尝试运用这些技巧,你会发现自己解决问题的能力提升了一个台阶。