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}'

总结

重定向核心要点

  1. 标准输出重定向>(覆盖)、>>(追加)
  2. 标准错误重定向2>2>>
  3. 合并重定向&>2>&1
  4. 输入重定向<<<(Here Document)、<<<(Here String)

管道关键规则

  • 只传递标准输出,不传递标准错误
  • 使用2>&1|& 让错误信息通过管道
  • 管道中的命令并行执行,数据流式处理

最佳实践

  • 使用tee同时查看和保存输出
  • 重要操作记录详细日志(&>> logfile
  • 使用/dev/null丢弃不需要的输出
  • 复杂的重定向分步调试

掌握重定向和管道是Linux系统管理的核心技能,能够极大提高工作效率和脚本能力。

作者:严锋  创建时间:2025-11-01 11:52
最后编辑:严锋  更新时间:2025-11-04 14:01