Linux操作系统-shell编程之sed、awk、find、printf
一、 前言
在Linux系统运维和日常开发工作中,高效处理文本数据是提升工作效率的关键。grep、sed、awk与find作为Linux文本处理“四剑客”,各自在特定场景下发挥着不可替代的作用。掌握这些工具不仅能简化重复性任务,还能实现复杂的文本分析和格式化输出。
学习路径建议:建议读者按照“基础语法→常用选项→实战案例→综合应用”的顺序逐步深入。首先熟悉每个工具的基本命令格式和核心参数,然后通过实际示例理解其工作原理,最后结合正则表达式等高级技巧解决真实场景中的问题。grep在之前的文章已经介绍过,本文将从sed开始,依次介绍printf、awk和find,帮助您构建系统的文本处理知识体系。
在Linux系统管理和日志处理中,grep、sed、printf、awk、find是不可或缺的文本处理利器。它们能够高效地完成文本的过滤、查找、替换、格式化与结构化分析,极大地提升了运维和开发的效率。本文将对sed、find、awk、printf四个工具进行系统介绍:sed(流编辑器)擅长基于行的文本替换与过滤;printf 用于精确控制输出格式,生成规整的报告;awk 则是一个强大的文本分析工具,尤其适合处理表格型数据并执行计算;find 是 Linux 系统中功能强大的文件查找工具,并支持对找到的文件执行各种操作。通过本文的学习,您将掌握它们的基本语法、核心功能及典型应用场景。
一、sed:流式文本编辑器
sed(Stream Editor)是一个非交互式的流编辑器。它逐行读取文本,根据预设规则对每行进行处理,并将结果输出到标准输出,默认不修改原始文件。
1. 基本语法
sed [选项] '地址 命令' 文件名
- 地址:指定要操作的行,可以是行号(如
3)、范围(如2,5)或正则表达式(如/pattern/)。省略则作用于所有行。
2. 核心命令
① 替换(s)——最强大的功能
# 将每行第一个 "root" 替换为 "admin"
sed 's/root/admin/' /etc/passwd
# 全局替换:将所有 "nologin" 替换为 "disabled"
sed 's/nologin/disabled/g' /etc/passwd
# 忽略大小写替换
sed 's/error/warning/gi' app.log
# 给所有数字加上方括号(& 代表匹配到的整个内容)
echo "line 123 has number 456" | sed 's/[0-9][0-9]*/[&]/g'
# 输出:line [123] has number [456]
s 命令的语法是 s/原模式/替换内容/[标志]。g 标志表示全局替换(每行所有匹配项),i 表示忽略大小写。
② 删除(d)
# 删除第3行
sed '3d' file.txt
# 删除第2到第5行
sed '2,5d' file.txt
# 删除所有包含 "nologin" 的行
sed '/nologin/d' /etc/passwd
# 删除所有空行(含空格)
sed '/^[[:space:]]*$/d' file.txt
③ 打印(p)——配合 -n 使用
# 只打印第3行
sed -n '3p' file.txt
# 只打印包含 "root" 的行(效果类似 grep)
sed -n '/root/p' /etc/passwd
-n 选项会禁止 sed 的默认输出,只打印被 p 明确指定的行。
④ 插入与追加(i 和 a)
# 在第3行前面插入 "Hello"
sed '3i\Hello' file.txt
# 在第3行后面追加 "World"
sed '3a\World' file.txt
3. 高级技巧
分组替换(反向引用) :
# 将日期从 YYYY-MM-DD 改为 DD/MM/YYYY,此处使用了后向引用,\3表示第三个小括号匹配的内容,\2、\1以此类推。
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/g' date.txt
利用正则表达式的分组功能(()),可以在替换时引用原文本的部分内容(\1、\2…),常用于数据格式转换。
直接修改文件(-i) :
# 危险:直接修改且无备份
sed -i 's/old/new/g' file.txt
# 安全:先备份再修改
sed -i.bak 's/old/new/g' file.txt
-i.bak 会创建一个 .bak 后缀的备份文件,强烈建议养成这个习惯。
模式空间、保持空间与多行文本处理处理 :
| 空间 | 名称 | 本质 | 生命周期 |
|---|---|---|---|
| Pattern Space(模式空间) | 工作台(CPU缓存) | 当前正在处理的那一行(或几行)内容 | 每轮循环清空重建 |
| Hold Space(保持空间) | 仓库(硬盘备份) | 一个跨行循环持久保留的备用缓冲区 | 初始为空,除非你主动操作,否则数据一直保留 |
sed 的默认流程是:读一行 → 放入 Pattern Space → 执行命令 → 打印 Pattern Space → 清空 Pattern Space → 读下一行。而多行命令和保持空间命令,就是用来打破这个默认流程的。
多行文本处理命令速查表
| 命令 | 作用对象 | 详细说明 |
|---|---|---|
N |
Pattern Space | 读取下一行追加到 Pattern Space(当前行 + \n + 下一行) |
p |
Pattern Space | 小写 p:打印整个 Pattern Space(不管有没有换行符) |
P |
Pattern Space | 大写 P:只打印 Pattern Space 中第一个 \n 之前的内容(即多行中的第一行) |
d |
Pattern Space | 小写 d:删除整个 Pattern Space,立即开始下一轮循环(读新行) |
D |
Pattern Space | 大写 D:删除 Pattern Space 中第一个 \n 之前的内容(即删掉第一行),若删后还有内容,不读新行,直接用剩余内容重启本轮循环 |
h |
→ Hold Space | 用 Pattern Space 的内容覆盖 Hold Space |
H |
→ Hold Space | 将 Pattern Space 追加到 Hold Space(原内容 + \n + 新内容) |
g |
Hold Space → | 用 Hold Space 的内容覆盖 Pattern Space |
G |
Hold Space → | 将 Hold Space 追加到 Pattern Space(原内容 + \n + 保持空间内容) |
多行文本处理举例
1、合并文件内容
文件内容如下:
admin
123456
user1
789123
user2
456789
audit
234567
需要将文件内容合并为如下形式
admin 123456
user1 789123
user2 456789
audit 234567
可以使用sed命令进行处理:sed ‘N; s/\n/ /g’ filename
三行合并为一行呢?可以使用如下方法进行合并:
文件内容:
admin
123456
/bin/bash
user1
789123
/bin/nologin
user2
456789
/bin/sh
audit
234567
/bin/disabled
合并命令:sed ‘N; N; s/\n/ /g; P; D;’ filename
文件所有内容合并为一行:
sed ‘:a; N; $!ba; s/\n//g;’ filename
所有行合并为一行有时会很有用,如你复制了一串秘钥,但是粘贴后由于格式问题变成了几行,这时可用该命令合并成一行。
2、反转文件内容
反转文件使用命令:sed ‘1!G; h; $!d’ filename
二、awk:文本分析与报表生成器
awk 并非简单的命令,而是一门专为文本处理设计的编程语言,诞生于贝尔实验室,名字取自三位创始人 Aho、Weinberger、Kernighan 的姓氏首字母。
1. 核心工作原理
awk 和 sed 最大的区别在于:sed 以“整行”为单位处理文本,而 awk 先将每行拆分为“字段”(默认用空格/制表符分隔),再按字段处理。
处理流程:逐行读取 → 字段拆分 → 条件匹配 → 执行操作。
2. 基本语法
awk '[选项] '模式 { 动作 }' 文件名
-F:指定字段分隔符(如-F:表示用冒号分隔)$1, $2, $3...:引用第1、2、3个字段,$0代表整行NR:当前行号NF:当前行的字段数量
3. 三大核心模块
awk 的脚本结构由三个可选模块组成:
| 模块 | 执行时机 | 作用 |
|---|---|---|
BEGIN |
读取文本前执行(仅1次) | 初始化变量、打印表头 |
| 主体 | 逐行读取时执行(每行触发) | 字段提取、条件判断 |
END |
读取文本后执行(仅1次) | 汇总统计、打印最终结果 |
4. 实战案例
① 打印指定字段
# 打印 /etc/passwd 的第一列(用户名)
awk -F: '{print $1}' /etc/passwd
# 打印第1和第3列
awk -F: '{print $1, $3}' /etc/passwd
② 条件筛选
# 打印 UID 大于 1000 的用户名
awk -F: '$3 > 1000 {print $1}' /etc/passwd
# 打印包含 "root" 的行
awk '/root/ {print $0}' /etc/passwd
③ 统计与计算
# 统计文件行数
awk 'END {print NR}' file.txt
# 计算某列的总和
awk 'BEGIN {sum=0} {sum += $3} END {print "Total:", sum}' data.txt
④ BEGIN/END 实战:生成报表
awk -F':' 'BEGIN {print "===== 用户列表 =====\n用户名\tUID"}
{print $1 "\t" $3}
END {print "===== 共 " NR " 个用户 ====="}' /etc/passwd

三、printf:格式化输出专家
printf 命令用于格式化输出,它比 echo 更强大、更精确,是编写可移植 Shell 脚本的首选工具。
1. 基本语法
printf "格式化字符串" [变量或值]
注意:printf 默认不会像 echo 那样自动添加换行符,需要手动加上 \n。
2. 常用格式符
| 格式符 | 说明 |
|---|---|
%s |
字符串 |
%d |
十进制整数 |
%f |
浮点数 |
%x |
十六进制整数 |
%o |
八进制整数 |
%% |
输出百分号本身 |
3. 实战案例
① 基本输出
printf "Hello, World!\n"
# 输出:Hello, World!
printf "我有 %d 个苹果,每个价格 %.2f 元。\n" 5 3.14
# 输出:我有 5 个苹果,每个价格 3.14 元。
② 对齐与宽度控制
# 左对齐(-)、指定宽度(10)
printf "%-10s %-8s %s\n" "姓名" "年龄" "城市"
printf "%-10s %-8d %s\n" "张三" 25 "北京"
printf "%-10s %-8d %s\n" "李四" 30 "上海"
输出:
姓名 年龄 城市
张三 25 北京
李四 30 上海
③ 与 awk 配合使用
# 用 awk 提取数据,用 printf 格式化输出
awk -F: '{printf "%-20s %-5s\n", $1, $3}' /etc/passwd | head -5
④ 输出到变量(-v)
# 将格式化结果存入变量
printf -v mydate "%s" "$(date +%Y-%m-%d)"
echo "Today is $mydate"
四、find:文件搜索之王
find 是 Linux 下最强大的文件搜索工具,支持按文件名、类型、大小、时间、权限、所有者等多维度组合条件搜索。
1. 基本语法
find [路径] [选项] [表达式]
2. 常用搜索条件
① 按文件名
# 查找所有 .txt 文件(区分大小写)
find /home -name "*.txt"
# 查找 .txt 文件(不区分大小写)
find /home -iname "*.TXT"
② 按文件类型
# 查找普通文件
find /var/log -type f -name "*.log"
# 查找目录
find /etc -type d -name "*conf*"
# 查找符号链接
find /usr/bin -type l
③ 按时间
Linux 中每个文件有三个时间戳:atime(访问时间)、mtime(修改时间)、ctime(状态改变时间)。
# 查找 7 天内修改过的文件(- 表示"不到")
find /var/log -mtime -7
# 查找 7 天前修改的文件(+ 表示"超过")
find /var/log -mtime +7
# 查找 1 小时内访问的文件
find /var -amin -60
④ 按文件大小
# 查找大于 100MB 的文件
find /var -size +100M
# 查找小于 1KB 的文件
find /tmp -size -1k
# 查找空文件
find /tmp -size 0
3. 组合条件搜索
find 支持逻辑运算符:-a(与)、-o(或)、!(非)。
# 查找大于 1MB 且最近 30 天内修改过的 .txt 文件
find /path -name "*.txt" -size +1M -mtime -30
# 查找 /var/log 下 7 天内修改且权限为 644 的日志文件
find /var/log -type f -mtime -7 -perm 644 -name "*.log"
# 查找普通文件或符号链接
find . -type f -o -type l
4. 对搜索结果执行操作(-exec)
# 查找所有 .sh 文件并搜索其中包含 "keyword" 的行
find . -type f -name "*.sh" -exec grep -Hn "keyword" {} +
# 批量删除所有 .tmp 文件
find /tmp -name "*.tmp" -delete
# 将 .log 文件移动到备份目录
find . -type f -name "*.log" -exec mv {} /backup/logs/ \;
小技巧:-exec {} + 比 -exec {} \; 效率更高,因为 + 会批量传递文件,减少命令调用次数。
5. 限制搜索深度
# 仅在当前目录搜索(不进入子目录)
find . -maxdepth 1 -type f -name "*.txt"
五、四大利器协同作战
这四个工具单独使用已经很强大了,但真正的威力在于组合使用。通过管道(|)将它们串联起来,可以实现“1+1+1+1 > 4”的效果。
案例一:查找大文件并格式化输出
# 查找 /var 下大于 100MB 的文件,格式化显示大小和路径
find /var -type f -size +100M -exec ls -lh {} \; | \
awk '{printf "%-10s %s\n", $5, $9}'

案例二:批量修改配置文件
# 查找所有 .conf 文件,将 "old_host" 替换为 "new_host"
find /etc -type f -name "*.conf" -exec sed -i.bak 's/old_host/new_host/g' {} \;
案例三:日志分析报表
# 从 access.log 中提取状态码并统计
awk '{print $9}' access.log | sort | uniq -c | sort -rn | \
printf "%-10s %s\n" "次数" "状态码"
awk '{printf "%-10d %s\n", $1, $2}'
案例四:查找并清理旧日志
# 查找 30 天前修改的 .log 文件,压缩归档
find /var/log -type f -name "*.log" -mtime +30 -exec gzip {} \;
案例五:统计访问nginx次数前两名的ip地址
awk -F' ' '{print $1}' /var/log/nginx/access.log |sort -nr |uniq -c |sort -nr -k 1 |head -2 |awk 'BEGIN{printf "%-10s %-10s\n","访问次数","ip地址"}{printf "%-15s %-10s\n",$1,$2}'

六、快速对照表
| 工具 | 定位 | 核心能力 | 典型场景 |
|---|---|---|---|
| sed | 流编辑器 | 按行替换、删除、插入 | 批量修改配置文件 |
| awk | 文本分析语言 | 按字段处理、统计、报表 | 日志分析、数据提取 |
| printf | 格式化输出 | 精确控制输出格式 | 脚本中生成格式化报告 |
| find | 文件搜索 | 多条件查找文件 | 定位文件、批量操作 |
七、避坑指南
- sed 忘记
-i:不加-i所有修改只是预览,原文件不变。 - sed 正则转义:默认使用基础正则,
+、?、{}需要转义。建议加-E使用扩展正则。 - awk 字段索引从 1 开始:
$0是整行,$1是第一列,没有$0作为第一列的说法。 - find 的时间符号:
-表示“不到”,+表示“超过”(如-mtime -7是 7 天内,-mtime +7是 7 天前)。 - printf 不自动换行:记得加
\n。
掌握这四个工具,你就能在日常的日志排查、配置修改、数据统计、文件管理中游刃有余。它们不是孤立的工具,而是一套可以灵活组合的文本处理武器库。从今天开始,试着在实际工作中多用管道将它们串联起来,你会发现效率的提升远超想象。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)