未记录事实核验 · 状态:现行内容

记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。

warning 适用边界与维护说明

本文适合在 GNU/Linux 与 Bash 环境中学习标准数据流、管道和重定向,并使用自建文件练习,不是可直接部署的生产日志处理方案。基础文件筛选用find匹配.csv文件;访问日志统计限定为第一列IP、第五列状态码的简化布局。tee 在管道中把输出传给下一命令,不一定直接显示到终端。实战流水线需满足正文指定的字段格式、输入文件可读和报告目录可写等条件,且未检查各步失败,末尾的完成提示不能作为成功依据。删除、原地修改及截断操作应先检查范围并备份;现有空字符分隔的 xargs 示例已处理文件名分隔问题。本文仅作结构审阅,未执行命令或进行外部事实、链接核验。

管道与重定向 - 组合命令的威力

abstract 本文概要

  • 管道 |:把命令串联起来
  • 输出重定向 > 和 >>:把输出保存到文件
  • 输入重定向 <:从文件读取输入
  • 错误重定向 2>&1:捕获错误信息
  • 实际数据处理组合技

理解 Linux 的数据流

Linux 命令有三个标准数据流:

标准输入 stdin  (0):命令从哪里读取输入(默认:键盘)
标准输出 stdout (1):命令把结果输出到哪里(默认:终端屏幕)
标准错误 stderr (2):命令把错误信息输出到哪里(默认:终端屏幕)

管道和重定向,就是重新配置这三个数据流的流向。


管道 |:把命令串联

管道(pipe)把前一个命令的标准输出,接到后一个命令的标准输入。

命令 A | 命令 B | 命令 C
  │         ↑        ↑
  └─stdout──┘        │
              └─stdout─┘

基础示例

# 按文件名匹配当前目录中的普通CSV文件(独立find示例)
find . -maxdepth 1 -type f -name '*.csv' -print

# 查看日志,过滤错误行,再统计数量
cat app.log | grep "ERROR" | wc -l
# 简化写法(cat + grep 可以直接用 grep):
grep -c "ERROR" app.log

# 查看进程,过滤特定程序
ps aux | grep "python"

# 列出目录,排序,查看最大的几个
du -sh /data/* | sort -rh | head -10

多命令串联

管道可以无限串联,每个命令只负责一件事:

# 分析访问日志:找错误 → 提取 IP → 排序 → 去重统计 → 取 TOP 10
# 简化日志字段:IP 时间戳 请求方法 URL 状态码 响应大小(字段均不含空格)
# 示例:192.168.1.1 2026-03-18T09:30:00 GET /api/user 500 1024
# 仅适用于上述第一列为IP、第五列为状态码的布局
awk '$5 == 500 {print $1}' access.log | sort | uniq -c | sort -rn | head -10

# 拆解理解:
# 1. awk检查第五列状态码为500并输出第一列IP(须符合指定日志布局)
#    awk同时完成筛选状态码与提取IP,不是两个独立管道阶段
# 3. sort                       → 排序,让相同值相邻,以便 uniq 汇总全文件中的重复值
# 4. uniq -c                    → 去重并统计每个 IP 出现次数
# 5. sort -rn                   → 按数量从大到小排序
# 6. head -10                   → 取前 10 行

tip 管道的威力

Linux 的设计哲学:每个命令只做一件事,但做好。组合起来就能完成复杂任务。 上面这个 6 层管道展示了日志筛选与聚合的组合方式。若将“10GB 日志几秒完成、Python 实现至少 20 行”作为比较假设,需要给出测试环境、日志特征和具体实现;这里不能据此作性能或代码量保证。

常用管道组合

# 去重统计(排序 + 去重统计 + 按数量排序)
sort | uniq -c | sort -rn

# 找出文件中重复的行
sort data.txt | uniq -d

# 找出文件中唯一的行(只出现一次)
sort data.txt | uniq -u

# 统计文件中不重复的行数
sort data.txt | uniq | wc -l

# 把结果分页显示(避免刷屏)
ls -la /data/ | less

# 实时过滤日志
tail -f app.log | grep "ERROR"

# 查找并处理(xargs 把管道结果转换为命令参数)
# 先检查匹配范围;以下命令会删除匹配文件,建议在测试目录练习
find /tmp -type f -name "*.tmp" -print0 | xargs -0 -r rm -f --
# GNU/Linux 示例:先确认匹配文件并备份;命令会原地修改 /etc 下的匹配文件
 grep -rlZ "old_config" /etc/ | xargs -0 -r sed -i.bak 's/old_config/new_config/g' --

输出重定向 > 和 >>

把命令的输出保存到文件,而不是打印到终端。

> 覆盖写入

# 把 ls 的输出保存到文件(会覆盖已有内容)
ls -la > file_list.txt

# 把 grep 的结果保存到文件
grep "ERROR" app.log > errors.txt

# 把统计结果保存
wc -l /data/*.csv > line_counts.txt

# 清空文件(用 > 重定向空内容)
> old_file.txt
# 等效于
cat /dev/null > old_file.txt

warning `>` 会覆盖文件

如果目标文件已存在,> 会清空它再写入。要追加而不是覆盖,用 >>。

>> 追加写入

# 把结果追加到文件末尾(不覆盖)
echo "2026-03-18: 处理完成" >> process.log

# 把多个文件的内容合并
cat file1.csv >> merged.csv
cat file2.csv >> merged.csv

# 日志记录(常用在 Shell 脚本中)
echo "$(date '+%Y-%m-%d %H:%M:%S') INFO: 开始处理数据" >> etl.log

组合示例

# 提取错误日志并保存,同时显示行数
grep "ERROR" app.log | tee errors.txt | wc -l
# tee写入errors.txt,并把错误行传给wc -l;终端显示wc统计的行数

# 每天的统计结果追加到月度报告
date_str=$(date +%Y-%m-%d)
echo "=== $date_str ===" >> monthly_stats.txt
awk -F',' '{ sum += $5 } END { print "总金额:", sum }' orders_${date_str}.csv >> monthly_stats.txt

输入重定向 <

把文件内容作为命令的输入。

# 基本用法(效果等同于 cat file | command)
sort < unsorted.txt
wc -l < data.csv

# 向数据库导入 SQL 文件(非常常用!)
mysql -u alice -p mydb < import.sql
psql -U alice -d mydb < import.sql

# 使用 Here Document(往命令里传多行文本)
cat << EOF > config.properties
host=10.0.0.100
port=3306
database=mydb
EOF
# << EOF 到 EOF 之间的内容,作为 cat 的输入,再通过 > 写入文件

# 更常见的 Here Document 用法:
mysql -u alice -p mydb << EOF
SELECT COUNT(*) FROM users;
SELECT SUM(amount) FROM orders WHERE date='2026-03-18';
EOF

tip Here Document 在 Shell 脚本中的应用

#!/bin/bash
# 脚本中执行多条 SQL
mysql -u alice -p"${DB_PASS}" ${DB_NAME} << EOF
INSERT INTO etl_log (date, status, rows) VALUES ('$(date +%Y-%m-%d)', 'success', $(wc -l < data.csv));
UPDATE job_config SET last_run='$(date)' WHERE job_name='daily_etl';
EOF

错误重定向 2> 和 2>&1

基础错误重定向

# 把错误信息重定向到文件(2 是 stderr 的文件描述符)
ls /nonexistent 2> error.log

# 把错误信息丢弃(重定向到 /dev/null,Linux 的"黑洞")
ls /nonexistent 2>/dev/null

# 同时保存标准输出和错误输出到不同文件
command > output.log 2> error.log

2>&1 合并输出和错误

# 把 stderr 合并到 stdout(最终都输出到同一个地方)
command > all_output.log 2>&1

# 含义:
# > all_output.log  → stdout 重定向到文件
# 2>&1             → stderr (2) 重定向到 stdout (1) 的目标,即也到文件

# 常见写法:把所有输出(含错误)保存到文件
python etl.py > run.log 2>&1

# 新版 bash 的简化写法(等效于上面)
python etl.py &> run.log

# 实时查看输出的同时保存到文件
python etl.py 2>&1 | tee run.log

note 数据工作场景:脚本日志记录

# 提交 Spark 任务,把所有输出保存到日志文件
spark-submit \
    --class com.example.DataJob \
    --master yarn \
    job.jar \
    > /var/log/spark/data_job_$(date +%Y%m%d).log 2>&1

# 提交后,用 tail -f 实时查看
tail -f /var/log/spark/data_job_20260318.log

/dev/null - Linux 的垃圾桶

/dev/null 是一个特殊文件,写入它的数据全部被丢弃,读取它什么都读不到。

# 丢弃错误信息(不想看到 Permission denied 报错)
find / -name "*.log" 2>/dev/null

# 丢弃所有输出(让命令静默运行)
command > /dev/null 2>&1

# 清空文件
cat /dev/null > file.txt
# 或
> file.txt

tip find 命令常配合 2>/dev/null

# 搜索整个系统的 log 文件
# 不加 2>/dev/null 会报大量 Permission denied
find / -name "*.log" -size +100M 2>/dev/null

tee - T 形管道(同时输出到终端和文件)

# 把结果既显示在终端,又保存到文件
command | tee output.txt

# 追加模式(不覆盖已有文件)
command | tee -a output.txt

# 实际应用:ETL 任务的运行输出,既看又存
./run_etl.sh 2>&1 | tee etl_$(date +%Y%m%d).log

实战:数据处理流水线

# 场景:每天处理用户行为日志,生成统计报告

# 1. 提取今天的日志(假设空白分隔的字段依次为:不含空格且以 YYYY-MM-DD 开头的时间戳、IP、状态码、页面)
today=$(date +%Y-%m-%d)
grep "^${today}" /var/log/app/user_behavior.log > /tmp/today_behavior.log

# 2. 过滤有效记录(状态码 200,去掉机器人 IP 段)
# 仅当本业务已确认 10.* 为需要排除的来源时使用此过滤条件
awk '$3 == 200 && $2 !~ /^10\./' /tmp/today_behavior.log > /tmp/valid_behavior.log

# 3. 统计各页面访问量,保存报告
awk '{print $4}' /tmp/valid_behavior.log \
    | sort \
    | uniq -c \
    | sort -rn \
    | head -20 \
    > /data/report/${today}_page_stats.txt

# 4. 统计独立 IP 数(不等同于独立用户数)
awk '{print $2}' /tmp/valid_behavior.log \
    | sort -u \
    | wc -l \
    >> /data/report/${today}_page_stats.txt

# 5. 清理临时文件
rm -f /tmp/today_behavior.log /tmp/valid_behavior.log

echo "报告生成完成:/data/report/${today}_page_stats.txt"

快速参考

符号 含义 示例
| 管道:前一个命令的输出 → 下一个命令的输入 ls | grep csv
> 输出到文件(覆盖) ls > list.txt
>> 追加到文件 echo done >> log.txt
< 从文件读取输入 sort < data.txt
2> 错误输出到文件 cmd 2> err.log
2>&1 错误合并到标准输出 cmd > all.log 2>&1
&> 所有输出到文件(bash 简写) cmd &> all.log
/dev/null 丢弃输出 cmd 2>/dev/null
tee 同时输出到终端和文件 cmd | tee out.txt

下一步


info 前置知识


掌握检查

  • [ ] 能解释管道 | 的本质:把左边命令的标准输出连接到右边命令的标准输入
  • [ ] 知道 > 和 >> 的区别:前者覆盖,后者追加;追加操作在写日志时更安全
  • [ ] 能用 cmd > output.log 2>&1 把标准输出和错误输出都重定向到同一个文件
  • [ ] 理解 tee 的用途:既把数据传给下一个管道,又同时保存到文件,调试时常用
  • [ ] 能用 cat /dev/null > log.txt 原地截断日志文件而不删除其路径(会丢失现有内容;删除路径不必然使已打开的文件描述符失效,对运行中日志应使用适合该程序的日志轮转方式)

实践练习

基础题:用一行管道命令实现:在 /var/log/ 下找出所有 .log 文件,统计各文件行数,并按行数从多到少排序,结果保存到 /tmp/log_stats.txt(提示:find + wc -l + sort)。

进阶题:用管道组合实现日志分析:从 /var/log/syslog(或任意日志)中提取包含 error 的行(不区分大小写),去掉重复行,统计总数,把结果用 tee 同时输出到终端和文件 /tmp/error_summary.txt。


下一步

#Linux #管道 #重定向 #Shell #数据处理