Linux 输入输出重定向详解
一、标准输入输出流基础
1.1 三种标准流
Linux系统为每个进程提供三个标准数据流:
| 流类型 | 文件描述符 | 默认方向 | 描述 |
|---|---|---|---|
| 标准输入 (stdin) | 0 | 键盘输入 | 程序读取数据的地方 |
| 标准输出 (stdout) | 1 | 屏幕输出 | 程序正常输出的地方 |
| 标准错误 (stderr) | 2 | 屏幕输出 | 程序错误信息输出的地方 |
1.2 文件描述符图示
进程
├── 0 (stdin) ← 从键盘读取
├── 1 (stdout) → 输出到屏幕
└── 2 (stderr) → 错误输出到屏幕二、输出重定向详解
2.1 标准输出重定向
基本语法
命令 > 文件 # 覆盖写入
命令 >> 文件 # 追加写入
实战示例
# 覆盖写入(清空原内容)
echo "Hello World" > output.txt
cat output.txt
# 输出:Hello World
# 再次写入会覆盖
echo "New Content" > output.txt
cat output.txt
# 输出:New Content(之前的内容被覆盖)
# 追加写入(保留原内容)
echo "Line 1" >> log.txt
echo "Line 2" >> log.txt
cat log.txt
# 输出:
# Line 1
# Line 2
# 命令输出重定向
ls -l > file_list.txt
date >> log.txt
whoami > current_user.txt
2.2 标准错误重定向
基本语法
命令 2> 文件 # 错误输出重定向(覆盖)
命令 2>> 文件 # 错误输出重定向(追加)
实战示例
# 成功命令(只有标准输出)
ls /home > success.txt
cat success.txt
# 输出:home目录列表
# 失败命令(产生错误输出)
ls /不存在的目录 2> error.txt
cat error.txt
# 输出:ls: 无法访问 '/不存在的目录': 没有那个文件或目录
# 混合输出示例
ls /home /不存在的目录
# 输出:
# /home 的内容(标准输出)
# ls: 无法访问...(标准错误)
# 分离标准输出和标准错误
ls /home /不存在的目录 > output.txt 2> error.txt
cat output.txt
# 输出:/home 的内容
cat error.txt
# 输出:错误信息
2.3 同时重定向标准输出和标准错误
方法1:分别重定向
命令 > 输出文件 2> 错误文件
方法2:合并重定向到同一文件
命令 > 文件 2>&1 # 方法A:错误合并到输出
命令 &> 文件 # 方法B:简写形式(推荐)
命令 >& 文件 # 方法C:另一种写法
实战示例
# 分别重定向
ls /home /不存在的目录 > list.txt 2> errors.txt
# 合并重定向到同一文件
ls /home /不存在的目录 &> combined.txt
cat combined.txt
# 包含标准输出和标准错误
# 追加模式
ls /tmp >> combined.txt 2>&1
# 丢弃所有输出(黑洞设备)
ls /不存在的目录 > /dev/null 2>&1
# 或者简写
ls /不存在的目录 &> /dev/null
2.4 高级输出重定向技巧
重定向到多个目标
# 使用tee同时输出到屏幕和文件
ls -l | tee file_list.txt
# 屏幕显示列表,同时保存到文件
# 追加模式
echo "New item" | tee -a file_list.txt
# 输出到多个文件
ls -l | tee file1.txt file2.txt file3.txt
进程替换输出
# 比较两个命令的输出
diff <(ls /bin) <(ls /usr/bin)
# 将命令输出作为文件输入
grep "pattern" <(cat file1.txt; cat file2.txt)
三、输入重定向详解
3.1 标准输入重定向
基本语法
命令 < 文件 # 从文件读取输入
实战示例
# 创建测试文件
echo -e "apple\nbanana\ncherry" > fruits.txt
# 输入重定向
wc -l < fruits.txt
# 输出:3(统计行数)
grep "apple" < fruits.txt
# 输出:apple
sort < fruits.txt
# 输出:按字母排序的水果列表
# 与管道对比
cat fruits.txt | wc -l # 使用管道
wc -l < fruits.txt # 使用输入重定向(更高效)
3.2 Here Document 输入
语法:<< 结束标记
命令 << 标记
输入内容...
标记
实战示例
# 多行输入
cat << EOF
第一行内容
第二行内容
第三行内容
EOF
# 传递给命令
wc -l << END
line1
line2
line3
END
# 输出:3
# 变量替换(双引号)
name="Alice"
cat << EOM
Hello $name
Today is $(date)
EOM
# 输出:Hello Alice\nToday is 当前日期
# 禁止变量替换(单引号)
cat << 'EOM'
Hello $name
Today is $(date)
EOM
# 输出:Hello $name\nToday is $(date)
3.3 Here String 输入
语法:<<< 字符串
命令 <<< "字符串内容"
实战示例
# 字符串直接作为输入
wc -w <<< "Hello World Linux"
# 输出:3(3个单词)
grep "Hello" <<< "Hello World"
# 输出:Hello
tr 'a-z' 'A-Z' <<< "hello world"
# 输出:HELLO WORLD
# 变量扩展
name="John"
grep "John" <<< "Hello John Smith"
# 输出:John
四、管道中的流处理
4.1 管道只传递标准输出
重要规则
管道(|)只传递标准输出(stdout),不传递标准错误(stderr)
示例演示
# 成功命令 - 标准输出通过管道
ls /home | wc -l
# 输出:home目录下的文件数量
# 失败命令 - 只有标准错误,管道为空
ls /不存在的目录 | wc -l
# 输出:0(因为标准错误不通过管道)
# 同时屏幕显示:ls: 无法访问...(标准错误直接显示)
# 混合情况
ls /home /不存在的目录 | wc -l
# 输出:home目录的文件数量(只有标准输出通过管道)
# 屏幕显示错误信息(标准错误直接显示)
4.2 让标准错误通过管道
方法:合并流再管道
# 将标准错误重定向到标准输出,然后通过管道
ls /不存在的目录 2>&1 | wc -l
# 输出:1(错误信息作为一行通过管道)
ls /home /不存在的目录 2>&1 | wc -l
# 输出:home目录文件数 + 错误行数
# 简写形式
ls /不存在的目录 |& wc -l
实战应用
# 错误信息也参与处理
find /etc -name "*.conf" 2>&1 | grep -v "权限不够" | wc -l
# 日志分析包含错误信息
application_start 2>&1 | tee log.txt | grep -E "(ERROR|WARN)"
4.3 分离管道中的流
高级技巧:使用文件描述符
# 创建测试命令
{
echo "标准输出: 正常信息"
echo "标准错误: 错误信息" >&2
} > stdout.txt 2> stderr.txt
# 分别处理不同流
cat stdout.txt | wc -l # 只处理标准输出
cat stderr.txt | grep "错误" # 只处理标准错误
五、综合实战案例
5.1 完整的日志处理系统
#!/bin/bash
# log_processing_system.sh
LOG_FILE="/var/log/application.log"
OUTPUT_DIR="./logs_analysis"
mkdir -p "$OUTPUT_DIR"
echo "开始处理日志文件: $LOG_FILE"
# 复杂的重定向和管道组合
cat "$LOG_FILE" |
# 分离不同类型的日志
tee \
>(grep "ERROR" > "$OUTPUT_DIR/errors.txt") \
>(grep "WARN" > "$OUTPUT_DIR/warnings.txt") \
>(grep "INFO" > "$OUTPUT_DIR/info.txt") \
| \
# 主要处理流程
awk '{print $1, $4, $5}' |
sort -k2 |
tee "$OUTPUT_DIR/processed.txt" |
wc -l > "$OUTPUT_DIR/total_lines.txt"
# 生成报告
echo "=== 日志分析报告 ==="
echo "总行数: $(cat "$OUTPUT_DIR/total_lines.txt")"
echo "错误数: $(wc -l < "$OUTPUT_DIR/errors.txt")"
echo "警告数: $(wc -l < "$OUTPUT_DIR/warnings.txt")"
echo "信息数: $(wc -l < "$OUTPUT_DIR/info.txt")"
5.2 安全备份脚本
#!/bin/bash
# secure_backup.sh
BACKUP_SRC="/important/data"
BACKUP_DEST="/backup/$(date +%Y%m%d)"
LOG_FILE="/var/log/backup.log"
echo "开始备份: $(date)" | tee -a "$LOG_FILE"
# 备份操作,详细记录日志
tar -czf - "$BACKUP_SRC" 2>&1 |
tee \
>(pv -s $(du -sb "$BACKUP_SRC" | cut -f1) > "$BACKUP_DEST.tar.gz") \
>(grep -v "已移除前导" > backup_errors.txt) \
| \
# 记录到主日志
tee -a "$LOG_FILE" |
# 实时显示进度
while read line; do
echo "[$(date +%H:%M:%S)] $line"
done
# 验证备份
if tar -tzf "$BACKUP_DEST.tar.gz" &> /dev/null; then
echo "备份成功: $(date)" | tee -a "$LOG_FILE"
md5sum "$BACKUP_DEST.tar.gz" >> "$LOG_FILE"
else
echo "备份失败: $(date)" | tee -a "$LOG_FILE"
cat backup_errors.txt >> "$LOG_FILE"
fi
5.3 系统监控面板
#!/bin/bash
# system_monitor_dashboard.sh
# 清屏函数
clear_screen() {
printf "\033c"
}
# 实时监控循环
while true; do
clear_screen
echo "🖥️ 系统实时监控面板"
echo "更新时间: $(date)"
echo "═"$(printf '═%.0s' {1..60})"═"
echo
# 使用重定向和管道收集系统信息
{
# CPU使用率
echo "💻 CPU: $(top -bn1 | grep "Cpu(s)" | awk '{print $2}')%"
# 内存使用
free -h | grep Mem | awk '{print "🧠 内存: " $3 "/" $2 " (" $3/$2 * 100 "%)"}'
# 磁盘使用
df -h / | awk 'NR==2{print "💾 根目录: " $3 "/" $2 " (" $5 ")"}'
# 负载平均值
echo "📊 负载: $(uptime | awk -F'load average:' '{print $2}')"
# 进程数
echo "🔄 进程: $(ps aux | wc -l)"
# 网络连接
echo "🌐 连接: $(netstat -an | grep ESTABLISHED | wc -l)"
} 2>/dev/null | tee >(logger -t system_monitor) | column -t
sleep 2
done
六、高级重定向技巧
6.1 文件描述符操作
自定义文件描述符
# 创建自定义文件描述符
exec 3> custom_output.txt
echo "这是自定义输出" >&3
exec 3>&- # 关闭文件描述符
# 同时读写
exec 4<> bidirectional.txt
echo "写入数据" >&4
cat <&4
exec 4>&-
文件描述符重定向
# 保存和恢复标准输出
exec 5>&1 # 保存stdout到fd5
exec 1> output.txt
echo "这行写入文件"
exec 1>&5 # 恢复stdout
echo "这行显示在屏幕"
# 临时重定向
{
echo "正常输出"
echo "错误输出" >&2
} > stdout.txt 2> stderr.txt
6.2 网络重定向
重定向到网络套接字
# 发送数据到网络
echo "Hello Server" | nc localhost 8080
# 从网络读取数据
nc -l 8080 > received_data.txt &
# 发送文件
cat large_file.txt | nc remote-server 8080
TCP/UDP重定向
# 使用重定向进行网络通信
exec 3<>/dev/tcp/google.com/80
echo -e "GET / HTTP/1.1\r\nHost: google.com\r\n\r\n" >&3
cat <&3
七、常见问题与调试
7.1 重定向常见错误
错误1:重定向顺序问题
# 错误:重定向顺序影响结果
command > file 2>&1 # ✓ 正确:先重定向输出,再重定向错误
command 2>&1 > file # ✗ 错误:顺序不对
# 测试
ls /不存在的目录 > output.txt 2>&1
cat output.txt # 包含错误信息
ls /不存在的目录 2>&1 > output.txt
cat output.txt # 可能为空或不全
错误2:管道中的流混淆
# 错误理解管道中的流
ls /不存在的目录 | grep "abc"
# grep可能找不到内容,因为错误信息不通过管道
# 正确做法
ls /不存在的目录 2>&1 | grep "没有那个文件"
7.2 调试技巧
逐步调试重定向
# 方法1:使用tee调试
complex_command | tee debug_output.txt | next_command
# 方法2:分步执行
complex_command > step1.txt
cat step1.txt | next_command > step2.txt
# 方法3:检查文件描述符
ls -l /proc/$$/fd # 查看当前进程的文件描述符
重定向状态检查
# 检查重定向是否生效
echo "测试" > file.txt
echo "退出状态: $?" # 0表示成功
# 检查文件是否可写
{ echo "测试" > file.txt; } 2>/dev/null && echo "可写" || echo "不可写"
# 检查磁盘空间
df -h . | awk 'NR==2{print "可用空间: " $4}'
总结
重定向核心要点
- 标准输出重定向:
>(覆盖)、>>(追加) - 标准错误重定向:
2>、2>> - 合并重定向:
&>、2>&1 - 输入重定向:
<、<<(Here Document)、<<<(Here String)
管道关键规则
- 只传递标准输出,不传递标准错误
- 使用
2>&1或|&让错误信息通过管道 - 管道中的命令并行执行,数据流式处理
最佳实践
- 使用
tee同时查看和保存输出 - 重要操作记录详细日志(
&>> logfile) - 使用
/dev/null丢弃不需要的输出 - 复杂的重定向分步调试
掌握重定向和管道是Linux系统管理的核心技能,能够极大提高工作效率和脚本能力。
作者:严锋 创建时间:2025-11-01 11:52
最后编辑:严锋 更新时间:2025-11-04 14:01
最后编辑:严锋 更新时间:2025-11-04 14:01