Linux 常用命令与文本三剑客教程:从入门到进阶
引言:为何要精通命令行?
在图形化界面(GUI)主导的今天,为何我们仍需回归看似“原始”的命令行(CLI)?对于工程师而言,命令行不仅是操作系统暴露的底层接口,更是一种高效、可编程、可复现的通用“语言”。它能让你:
- 自动化重复任务:通过脚本批量处理文件、部署应用、监控系统,极大提升效率。
- 深入理解系统:直接与操作系统交互,观察进程、网络、文件系统的真实状态,培养“庖丁解牛”般的洞察力。
- 驾驭无界面环境:在服务器、容器、CI/CD 等环境中,命令行是唯一且最高效的交互方式。
- 构建强大工具链:将多个简单命令通过管道组合,以“流”的方式处理数据,构建出远超单个 GUI 工具能力的强大工作流。
本教程旨在帮助已有一定开发经验的工程师,系统性地掌握 Linux 环境下最高频的命令、I/O 与管道思想,以及被誉为“文本三剑客”的 grep、sed、awk 的核心用法与思维模型。无论你是后端、数据、还是平台工程师, mastery of these tools will prove to be a significant force multiplier in your daily work.
一、 命令行基础与通用技能
本章节覆盖了日常工作中 80% 的高频命令,掌握它们是高效操作 Linux 系统的基石。
1. 路径与文件管理
核心心法:在 Linux 中,“一切皆文件”。目录是特殊的文件,设备也是文件。理解文件系统层次结构与路径的表达方式是第一步。
命令 | 核心功能 | 常用示例 |
|---|---|---|
ls | 列出目录内容 | ls -alF:显示所有文件(含隐藏)、长格式、并标记类型。 |
cd | 切换当前目录 | cd ~ (回家), cd - (回上个目录), cd ../ (去上级目录)。 |
pwd | 显示当前工作目录 | pwd:打印绝对路径。 |
tree | 以树状图显示目录结构 | tree -L 2 -d:仅显示目录,最大深度为 2。 |
mkdir | 创建目录 | mkdir -p project/src/main:递归创建多层目录。 |
rm / rmdir | 删除文件/空目录 | rm -rf build/ (危险!强制递归删除), rmdir empty_dir。 |
cp / mv | 复制/移动或重命名 | cp -r src/ build/ (复制目录), mv old.txt new.txt (重命名)。 |
ln | 创建链接 | ln -s /usr/bin/python3 python (软链接), ln file.txt hard_link (硬链接)。 |
通配符 (glob) | 匹配文件名模式 | ls *.log (*), cat data_?.csv (?), cp assets/[a-z]*.png ([]). |
隐藏文件 | 以 . 开头的文件 | ls -a 或 ls -A (不显示 . 和 ..) 可查看。 |
软链接 (Symbolic Link) vs. 硬链接 (Hard Link)
- 软链接:像一个快捷方式,存储的是指向源文件的路径。删除源文件,链接失效。
- 硬链接:同一个文件内容的多个名字。所有硬链接共享 inode 和数据块,删除任意一个链接不影响文件实体,直到最后一个链接被删除。
2. 内容查看与文本工具
命令 | 核心功能 | 常用示例 |
|---|---|---|
cat / tac | 查看全部内容 / 倒序查看 | cat file.txt,tac file.txt。 |
head / tail | 查看文件头部/尾部 | head -n 20 file.log, tail -f app.log (实时监控)。 |
less / more | 分页查看文件内容 | less large_file.csv (推荐, 功能更强, 可前后翻页)。 |
nl | 添加行号 | nl script.sh。 |
wc | 统计字数、行数、字节数 | wc -l file.txt (仅统计行数)。 |
cut | 按列或字段切分文本 | cut -d',' -f1,3 data.csv (CSV 中以逗号分隔,取第1和3列)。 |
paste | 按列合并文件 | paste file1.txt file2.txt。 |
tr | 字符转换或删除 | tr 'a-z' 'A-Z' < input.txt (转大写), tr -d '\r' < win.txt (删回车符)。 |
sort / uniq | 排序 / 去重(相邻行) | sort data.txt | uniq -c (排序、去重并统计次数)。 |
tee | T 型管道,分流输出 | ls -l | tee file.list | wc -l (列表存文件并统计行数)。 |
3. 搜索与查找
命令 | 核心功能 | 常用示例与参数 |
|---|---|---|
grep | 在文本中搜索模式 | grep -rni 'ERROR' /var/log/ (递归、忽略大小写、显示行号搜索)。后续详述。 |
find | 按条件查找文件 | find . -type f -name "*.java" (找Java文件)find / -size +1G (找大于1G的文件)find . -mtime -7 (7天内修改过的)find . -path "./target" -prune -o -name "*.rs" -print (排除target目录)find . -name "*.log" -exec rm {} \; (找到即删,低效)find . -name "*.log" -exec rm {} + (批量删除,高效) |
locate / updatedb | 基于数据库快速查找文件 | sudo updatedb && locate my_config.conf。 |
xargs | 将标准输入转为命令行参数 | find . -name "*.tmp" -print0 | xargs -0 rm (安全处理含空格文件名)cat urls.txt | xargs -I {} curl -O {} (逐行下载URL)ls *.gz | xargs -P 4 -n 1 tar -xzf (4进程并发解压) |
4. 压缩与归档
命令 | 核心功能与常见组合 | 常用示例 |
|---|---|---|
tar | 归档工具 (打包/解包) | tar -cvf archive.tar dir/ (打包)tar -xvf archive.tar (解包)tar -czvf archive.tar.gz dir/ (打包并用 gzip 压缩)tar -xzvf archive.tar.gz (用 gzip 解压并解包)tar -cjvf archive.tar.bz2 dir/ (bzip2 压缩)tar -xjvf archive.tar.bz2 (bzip2 解压)tar -cJvf archive.tar.xz dir/ (xz 压缩, 压缩率最高)tar -xJvf archive.tar.xz (xz 解压) |
gzip / bzip2 / xz | 压缩/解压单个文件 | gzip file.log -> file.log.gz; gunzip file.log.gz。 |
zip / unzip | 跨平台压缩/解压 | zip -r archive.zip dir/; unzip archive.zip。 |
5. 进程与作业
命令 | 核心功能 | 常用示例 |
|---|---|---|
ps | 查看进程快照 | ps aux (BSD 风格) 或 ps -ef (System V 风格)。 |
top / htop | 实时监控进程动态 | htop (推荐, 交互式,色彩丰富)。 |
pgrep / pkill | 按名查找/杀死进程 | pgrep -u mojucheng java; pkill -9 -f 'data-process'。 |
kill / killall | 发送信号给进程 (默认 SIGTERM) | kill 12345; kill -9 12345 (强制终止); killall nginx。 |
jobs / fg / bg | 管理后台作业 | ctrl+z (暂停), bg (转后台), jobs (查看), fg %1 (调回前台)。 |
nice / renice | 调整进程优先级 | nice -n 10 ./my_job; renice -n 5 -p 12345。 |
6. 磁盘与文件系统
命令 | 核心功能 | 常用示例 |
|---|---|---|
df / du | 查看磁盘/目录空间占用 | df -h (人类可读); du -sh * (当前目录各文件/夹大小)。 |
lsblk / blkid | 列出块设备/查看 UUID | lsblk; sudo blkid。 |
mount / umount | 挂载/卸载文件系统 | sudo mount /dev/sdb1 /mnt/data; sudo umount /mnt/data。 |
chmod / chown / chgrp | 修改权限/所有者/所属组 | chmod 755 script.sh; chmod u+x script.sh; chown -R user:group dir/。 |
umask | 设置默认文件权限掩码 | umask 022 (文件 644, 目录 755)。 |
setfacl / getfacl | 设置/获取文件访问控制列表 (ACL) | setfacl -m u:otheruser:rwx file; getfacl file。 |
7. 用户与身份
命令 | 核心功能 | 常用示例 |
|---|---|---|
useradd / userdel / usermod | 增/删/改用户 | sudo useradd -m -s /bin/bash newuser; sudo usermod -aG docker user。 |
passwd | 修改密码 | passwd (修改自己); sudo passwd user (修改他人)。 |
id / groups / sudo | 查看身份/所属组/以 root 执行 | id mojucheng; sudo -l (查看可执行的 sudo 命令)。 |
8. 网络与诊断
命令 | 核心功能 | 常用示例 |
|---|---|---|
ip | 现代网络配置工具 (替代 ifconfig) | ip addr show; ip route。 |
ss / netstat | 查看网络连接/套接字统计 | ss -tuln (推荐, 更快); netstat -anp (经典)。 |
ping / traceroute | 网络连通性测试 / 路由追踪 | ping baidu.com; traceroute baidu.com。 |
nc (netcat) | 网络工具中的“瑞士军刀” | nc -zv host 80 (端口扫描); echo "hello" | nc host 1234 (发数据)。 |
curl / wget | HTTP 请求与文件下载 | curl -L http://example.com; wget -c http://example.com/file.zip。 |
dig / nslookup | DNS 查询 | dig A baidu.com; nslookup baidu.com。 |
9. 日志与系统服务
命令 | 核心功能 | 常用示例 |
|---|---|---|
journalctl | 查询 systemd 日志 | journalctl -u nginx.service -f (实时看 Nginx 日志)。 |
systemctl | 管理 systemd 服务 | systemctl status nginx; systemctl start/stop/restart/enable/disable nginx。 |
timedatectl | 管理系统时间与时区 | timedatectl set-timezone 'Asia/Shanghai'。 |
crontab / at | 计划任务 / 一次性任务 | crontab -e (编辑); at 2pm + 3 days (指定未来时间)。 |
10. Shell I/O 与组合
核心心法:这是命令行的精髓所在!将命令视为处理“数据流”的节点,通过 I/O 操作将它们灵活地“焊接”在一起。
- 标准输入/输出/错误 (stdin/stdout/stderr)
stdin(文件描述符 0): 默认是键盘输入。stdout(文件描述符 1): 默认是屏幕输出。stderr(文件描述符 2): 默认是屏幕输出,用于错误信息。
- 重定向 (Redirection)
>: 将stdout重定向到文件 (覆盖)。command > file.out>>: 将stdout重定向到文件 (追加)。command >> file.log<: 将文件内容作为stdin。command < file.in2>: 将stderr重定向到文件。command 2> file.err2>&1: 将stderr重定向到stdout。&>: (bash 简写) 将stdout和stderr都重定向。command &> file.all
- 管道 (Pipe
|)- 将前一个命令的
stdout连接到后一个命令的stdin。 - 示例:
cat access.log | grep '404' | wc -l(统计访问日志中 404 错误的数量)。
- 将前一个命令的
- 子进程与命令替换
$(command)或`command`: 将命令的stdout作为变量值或参数。- 示例:
echo "Today is $(date)"
- 导出与引用
export VAR=value: 设置环境变量,使其在子 shell 中可用。- 单引号
'': 强引用,内部所有字符都视为字面量。echo 'Hello $USER'->Hello $USER - 双引号
"": 弱引用,$、\``、\` 会被解析。echo "Hello $USER"->Hello mojucheng
二、 文本三剑客系统讲解:grep, sed, awk
如果说管道是命令行的“龙骨”,那么 grep, sed, awk 就是龙骨上最锋利的“三齿”。它们分别代表了文本处理的三个核心能力:筛选 (Searching)、编辑 (Editing) 和 报告 (Reporting)。
1. grep:文本筛选大师 (Global Regular Expression Print)
grep 的核心任务是从文本流中筛选出匹配特定模式的行。
参数/概念 | 核心功能 | 常用示例 |
|---|---|---|
-E, -P | 使用扩展正则(ERE) / Perl兼容正则(PCRE) | grep -E 'warn|error' (匹配 warn 或 error), grep -P '\d{3}' (匹配三位数字)。 |
-w, -x | 匹配整个单词 / 匹配整行 | grep -w 'is' (不匹配 this), grep -x 'root:.*' (精确匹配以 root: 开头的行)。 |
-o, -n, -r, -l | 仅显示匹配部分 / 显示行号 / 递归搜索 / 仅显示文件名 | grep -o '[0-9.]\+' access.log (提取 IP), grep -rl 'TODO' . (查找含 TODO 的文件)。 |
-v | 反向匹配 (不包含模式的行) | ps aux | grep -v 'grep' (过滤掉 grep 进程本身)。 |
-A, -B, -C | 显示匹配行的后/前/上下文 N 行 | grep -C 2 'Exception' app.log。 |
--include, --exclude | 递归搜索时包含/排除特定文件 | grep -r --include='*.py' --exclude='*_test.py' 'import os' . |
正则表达式方言 (BRE/ERE/PCRE)
- BRE (Basic): 默认模式,
?,+,{},|,()等需转义,如\\(a\\|b\\)。 - ERE (Extended,
-E): 无需转义?,+,{},|,()。更常用,推荐。 - PCRE (Perl-compatible,
-P): 功能最强,支持\d,\s,look-around等高级语法,但并非所有grep版本都支持。
常见正则速查
^: 行首,$: 行尾.: 任意单字符,*: 前项 0 或多次,+: 前项 1 或多次,?: 前项 0 或 1 次[abc]: 字符集,[^abc]: 排除字符集{n},{n,},{n,m}: 精确/范围重复次数(a|b): 分组或,\b: 单词边界
2. sed:流编辑器 (Stream Editor)
sed 是一种非交互式的行编辑器,它逐行读取文本,应用指定的编辑命令,然后输出结果。核心是 地址{命令} 的模式。核心心法:sed 的世界里,你是一位“流水线工人”,面对传送带上(标准输入)源源不断的产品(文本行),你根据图纸(脚本命令)对其进行修改、丢弃或复制,然后放到另一条传送带上(标准输出)。
命令/概念 | 核心功能 | 常用示例 |
|---|---|---|
地址与范围 | 指定命令作用的行 | sed '3d' file (删第3行), sed '/^#/d' file (删注释行), sed '1,10s/foo/bar/' file (1-10行替换)。 |
s/regexp/replacement/flags | 替换 (substitute) | sed 's/localhost/127.0.0.1/g' (全局替换)sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)/\2-\1/' (用 () 分组并后向引用 \1, \2)sed 's/USER=.*/USER=admin/i' (忽略大小写) |
d | 删除 (delete) | sed '/^$/d' file (删除空行)。 |
p | 打印 (print) | sed -n '/ERROR/p' file (与 -n 配合,仅打印匹配行)。 |
y/source/dest/ | 字符变换 (transform) | sed 'y/abc/ABC/' (a->A, b->B, c->C)。 |
a / i / c | 追加 / 插入 / 更改 | sed '/start/a \-- new line after --' file。 |
h/H/g/G/x | 保持/交换空间 (hold/get/exchange) | sed -n '1h; ${G;p;}' file (高级用法: 调换首末行)。 |
-n, -e, -i | 静默模式 / 多脚本 / 原地修改 | sed -n 'p' (取消默认输出), sed -e 's/a/A/' -e 's/b/B/', sed -i.bak 's/old/new/' file (修改并备份)。 |
安全替换直接使用 sed -i 有风险,推荐使用 sed -i.bak 创建备份,或者先预览结果再决定是否覆盖:sed 's/old/new/g' file > file.tmp && mv file.tmp file
3. awk:全能文本报告生成器
awk 不仅仅是一个工具,它本身就是一门编程语言。它将每一行文本按指定分隔符切分为字段,然后让你基于模式对这些字段进行计算、处理和格式化输出。核心心法:awk 的世界里,你是一位“数据分析师”,面对一张张报表(文本行),你读取每个单元格(字段),进行计算、判断和聚合,最终生成一份新的、结构化的报告。awk 的基本模型是 PATTERN { ACTION }。对每一行,如果匹配 PATTERN,则执行 { ACTION }。核心变量与概念
变量/概念 | 解释与示例 |
|---|---|
FS, RS | 字段分隔符 (Field Separator), 记录分隔符 (Record Separator)。默认 FS 是空格/Tab,RS 是换行符。awk -F, (CSV)。 |
OFS, ORS | 输出字段/记录分隔符。 awk -F, 'BEGIN{OFS="\t"} {print $1,$2}' (输入逗号分隔,输出Tab分隔)。 |
NF, NR, FNR | 当前行的字段数 (Number of Fields), 已处理的总行数 (Number of Records), 当前文件的行数 (File Number of Records)。 |
$0, $1, $2... | $0 代表整行,$1, $2 代表第 1、2 个字段。 |
BEGIN, END | BEGIN 在处理第一行前执行 (初始化),END 在处理完最后一行后执行 (汇总)。
awk 'BEGIN{print "START"} {print} END{print "END"}' |
内建函数与编程结构
- 字符串函数:
length(),substr(),split(),match(),gsub()/gensub() - 算术运算:
+ - * / % - 控制流:
if/else,while,for - 数组 (关联数组):
awk的数组是关联数组 (类似 Python 的 dict 或 Java 的 Map)。awk '{counts[$1]++} END{for(ip in counts) print ip, counts[ip]}' access.log(统计各 IP 访问次数)
典型 awk 用法
场景 | 示例 |
|---|---|
列选择与重排 | ls -l | awk '{print $9, $5}' (打印文件名和大小)。 |
条件筛选 | awk '$3 > 100' data.txt (打印第3列大于100的行)。 |
计算与聚合 | cat scores.txt | awk '{sum+=$1} END{print "Average:", sum/NR}' (计算平均分)。 |
格式化输出 | df -h | awk 'NR>1 {printf "Usage of %s is %s\n", $6, $5}'。 |
处理 CSV | awk -F, '$2 == "admin" {print $1, $4}' users.csv。 |
GNU awk (gawk) 进阶特性
gensub(): 更强大的替换函数,可指定替换第几次出现。strftime(): 格式化时间戳。@include: 包含其他 awk 脚本。ENVIORN: 访问环境变量。- 真正的多维数组。
三、 进阶范式与高效实践
掌握单个命令是基础,将它们组合成优雅、高效、健壮的工作流,才是真正拉开差距的地方。
1. 管道化思维:构建你的数据处理流水线
忘掉繁琐的 GUI 点击操作,将你的任务分解为标准的“生产-消费”流程。这种思维模型具有极强的复用性和扩展性。核心流程:产生数据 → 筛选 → 转换 → 聚合 → 格式化 → 输出暂时无法在飞书文档外展示此内容
- 产生数据:
cat,ls,curl,journalctl,ps... 任何能产生文本输出的命令。 - 筛选:
grep找出你关心的行。 - 转换:
sed清洗、归一化数据;awk提取、重排字段;tr转换字符。 - 聚合:
sort | uniq -c排序、计数;awk使用数组进行复杂聚合。 - 格式化输出:
awk '{printf ...}'生成报告;tee分流保存中间结果。
2. find + xargs: 海量文件处理的黄金搭档
当需要对大量文件执行同一操作时(如删除、修改、压缩),find -exec ... {} \; 方式效率低下,因为它为每个文件都启动一个新进程。正确姿势:
find ... -exec ... {} +:find会将文件名凑成一批,然后一次性传给exec后面的命令,大大减少进程创建开销。find ... -print0 | xargs -0 ...: 终极方案,完美解决文件名中包含空格、换行符等特殊字符的问题。-print0:find以 NULL 字符 (\0) 分隔文件名输出。-0:xargs以 NULL 字符作为输入分隔符。
# 安全高效地删除所有临时文件
find . -type f -name "*.tmp" -print0 | xargs -0 rm
3. sort/uniq 与 LC_ALL=C
sort 的行为受本地化设置(Locale)影响,这可能导致排序结果不符合预期或性能下降。
LC_ALL=C: 强制sort使用最原始的、基于字节值的排序规则。- 优点 1: 加速。字节排序比按字典序快得多。
- 优点 2: 稳定。结果唯一确定,不受系统语言环境影响。
# 对大数据集进行排序和去重计数时,推荐使用
export LC_ALL=C
cat large_log.txt | sort | uniq -c | sort -nr # 找出出现频率最高的行
4. 大文件与流式处理
- 避免读入内存: 命令行工具天然善于流式处理,逐行读取,内存占用极低。多用管道,避免将大文件内容存入 shell 变量。
tee分支: 在长管道中,有时需要保存中间结果用于调试或存档。tee在此时非常有用。... | tee stage1.log | grep ... | tee stage2.log | ...- 并发处理:
xargs -P可以轻松实现并发。cat image_list.txt | xargs -P 8 -I {} convert {} {}.webp(使用 8 个进程并发转换图片格式)注意: 确保任务之间是独立的,没有共享状态或资源竞争。
5. 可见性与边界(概念类比)
虽然 Linux 没有像 Bazel 那样强制的包边界和可见性规则,但我们可以借鉴其思想来组织项目和脚本,形成逻辑上的“模块”。
- 目录结构: 将功能相关的脚本、配置文件放在同一目录下。
- 接口脚本: 在目录顶层提供一个或少数几个入口脚本 (类似
BUILD文件中的xx_library),内部实现细节对外部隐藏。 - 依赖管理: 尽量通过参数传递依赖(如配置文件路径),而不是依赖硬编码的全局路径或环境变量。这使得你的脚本更具可移植性和可测试性。
四、 实战示例与对照表
理论结合实践,才能真正掌握。
1. 三剑客速查对照表
这张表帮助你快速映射同一个需求在三剑客中的不同实现方式。
功能/概念 | grep | sed | awk |
|---|---|---|---|
核心模型 | 行匹配与打印 | 地址+命令,流式编辑 | 模式+动作,字段处理 |
匹配/筛选 | grep 'pattern' | sed -n '/pattern/p' | awk '/pattern/{print}' 或 awk '$N ~ /pattern/' |
替换 | (不直接支持) | sed 's/old/new/g' | awk '{gsub(/old/, "new"); print}' |
字段/列操作 | grep -o + 其他工具 | 复杂正则分组 s/(\w+):(\d+)/\2:\1/' | awk '{print $2, $1}' |
分组与聚合 | grep ... | sort | uniq -c | (不擅长, 需高级技巧) | awk '{counts[$1]++} END{...}' |
格式化输出 | (有限) | (有限, a/i/c 命令) | printf 函数 |
脚本能力 | 无 (仅正则) | 有限 (分支、循环) | 完整编程语言 (变量, 数组, 函数, 控制流) |
2. 高频命令速查表
类别 | 命令 | 核心参数 | 一行示例 |
|---|---|---|---|
文件 | ls | -a, -l, -h, -t, -r | ls -ltra:按时间倒序列出所有文件的详细信息。 |
find | -name, -type, -mtime, -size, -exec, -prune | find . -type f -name "*.py" -mtime -1:查找一天内修改过的 Python 文件。 | |
cp | -r, -p, -a, --parents | cp --parents src/com/app/Main.java dest/:连同父目录结构一起复制。 | |
文本 | grep | -i, -r, -n, -v, -o, -E, -C | grep -Eiron 'fail|denied' /var/log/:递归搜日志中 fail 或 denied。 |
sed | -i, -n, -e, s///, d, p | sed -i 's/api.example.com/api.prod.com/g' config.yaml:原地替换配置文件域名。 | |
awk | -F, BEGIN, END, NR, NF, print, printf | awk -F: '{print $1}' /etc/passwd:打印所有用户名。 | |
sort | -n, -r, -k, -u, -t | sort -t',' -k3 -nr data.csv | head -n 10:按第3列数字倒序排CSV,取Top 10。 | |
进程 | ps | aux, -ef, -o | ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | head:按内存使用率看进程。 |
top/htop | (交互式) | htop -u www-data:只看 www-data 用户的进程。 | |
pkill | -f, -u, -9 | pkill -9 -f "stale-worker":强制杀死名字含 "stale-worker" 的进程。 | |
网络 | ss | -t, -u, -l, -n, -p | ss -tlpn | grep ':80':查看谁在监听 80 端口。 |
curl | -X, -H, -d, -s, -o, -L | curl -s -X POST -H "Content-Type: application/json" -d '{"key":"val"}' http://api/v1/resource。 |
3. 两套可复制的最小实践
a) 日志分析:统计 Nginx 日志 Top 10 访问 IP
目标:从原始 Nginx access.log 中,找出访问量最高的 10 个 IP 地址。日志格式 (示例):192.168.1.1 - - [19/Apr/2026:10:00:00 +0800] "GET /index.html HTTP/1.1" 200 1234 ...一步到位管道命令:
cat access.log | awk '{print $1}' | sort | uniq -c | sort -nr | head -n 10
分解步骤:
cat access.log: 产生数据源。awk '{print $1}':awk默认以空格为分隔符,$1就是 IP 地址。(转换)sort: 对 IP 地址进行排序,为uniq做准备。uniq -c: 对已排序的 IP 进行去重,并统计每个 IP 出现的次数。(聚合)sort -nr:-n按数值排序,-r倒序。即按访问次数从高到低排序。(聚合/排序)head -n 10: 取出前 10 行,即 Top 10 结果。(格式化/输出)
b) CSV 处理:清洗并统计数据
目标:有一个 sales.csv 文件,统计每个城市的总销售额,并忽略无效数据行。sales.csv (示例):
ID,City,Product,Amount
1,Shanghai,Book,100
2,Beijing,Pen,10
# This is a comment
3,Shanghai,Book,150
,Invalid,Data,
4,Beijing,Paper,20
一步到位管道命令:
cat sales.csv | sed '/^#/d;/^,/d' | awk -F, 'NR > 1 {sales[$2]+=$4} END {for(city in sales) print city","sales[city]}' > city_sales.csv
分解步骤:
cat sales.csv: 产生数据源。sed '/^#/d;/^,/d': 使用sed清洗数据。/^#/d删除注释行,/^,/d删除以逗号开头的无效行。(转换/清洗)awk -F, 'NR > 1 {sales[$2]+=$4} END {for(city in sales) print city","sales[city]}':-F,: 设置字段分隔符为逗号。NR > 1: 跳过表头。{sales[$2]+=$4}: (核心聚合) 以城市名 ($2) 为 key,累加销售额 ($4) 到关联数组sales。END {...}: 所有行处理完毕后,遍历sales数组,打印 "城市,总销售额" 格式的结果。(格式化/输出)
> city_sales.csv: 将最终报告输出到新文件。
4. 易错点与替代方案
grep -P可用性:grep -P依赖 PCRE 库,在某些极简系统或 macOS 上可能默认不安装。可使用grep -E替代多数场景,或安装pcre包。- BSD/GNU 差异: macOS 默认的命令行工具 (如
sed,awk) 是 BSD 版本的,参数和行为与 Linux 上常见的 GNU 版本有差异。例如,BSDsed的-i必须提供备份文件后缀 (sed -i '.bak' ...)。可通过brew install gnu-sed安装 GNU 版本,并用gsed调用。 - 安全替换: 重申,直接
sed -i是危险操作。善用备份sed -i.bak,或预览sed ... file > file.tmp && mv file.tmp file。对于复杂替换,有时awk更安全可控。 - 文件名中的空格: 老生常谈,但极易出错。在
for循环或管道中处理文件名时,始终考虑使用find ... -print0 | xargs -0 ...或在 shell 脚本中设置IFS=$'\\n'。