Linux三剑客:grep、sed、awk 详解以及find区别
grep、sed、awk是Linux/Unix系统中经典的文本处理工具,合称“三剑客”。三者各有所长:
- grep:擅长文本搜索(过滤)。
- sed:擅长文本编辑(替换、删除等)。
- awk:擅长文本分析(格式化输出、计算等)
一、grep:文本搜索
1. 基本语法
- 用途:在文件内容中搜索匹配特定模式的文本(支持正则表达式)。
- 工作对象:文件内容(文本)。
- 常用选项:
选项 作用 示例 -i
忽略大小写 grep -i "error" file.log
-r
递归搜索目录 grep -r "TODO" ./src
-n
显示匹配行的行号 grep -n "warning" app.log
-v
反向匹配(输出不包含模式的行) grep -v "debug" output.log
-l
仅输出包含匹配项的文件名 grep -rl "FIXME" ./code
-E
启用扩展正则表达式(等价于 egrep
)`grep -E "err
2. 示例
我们假设存在名为file.txt的文件,对该文件进行grep命令操作,文件内容如下
hello world
Hello Universe
start here
mission complete
hallo and hello
goodbye end
执行以下命令:
# 在文件中搜索基础文本
grep "hello" file.txt # 输出:hello world, hallo and hello
grep -i "hello" file.txt # 输出:hello world, Hello Universe, hallo and hello
grep -n "hello" file.txt # 输出:1:hello world, 5:hallo and hello# 递归搜索目录
grep -r "TODO" ./src # 搜索./src目录下所有文件中的"TODO"# 使用正则表达式
grep "^start" file.txt # 搜索以start开头的行 → "start here"
grep "end$" file.txt # 搜索以end结尾的行 → "goodbye end"
grep "he[ae]llo" file.txt # 匹配hello或hallo → "hello world", "hallo and hello"
grep -E "hello|complete" file.txt # 匹配hello或complete → "hello world", "mission complete", "hallo and hello"
grep "[A-Z]" file.txt # 搜索包含大写字母的行 → "Hello Universe", "mission complete"
二、sed:文本替换与转换
1. 基本语法
-
用途:对输入流(文件或管道)进行文本替换、删除、插入、打印等操作
-
工作模式:逐行处理文本,默认输出到标准输出(不修改源文件)
-
常用选项:
选项 作用 示例 -e
指定编辑命令 sed -e 's/a/A/' file
-n
抑制默认输出(常与 p
命令配合)sed -n '1,3p' file
-i
原地修改文件(可选备份后缀) sed -i.bak 's/old/new/' file
-r
启用扩展正则表达式 sed -r 's/(abc)/\U\1/' file
-
常用命令:
命令 作用 示例 s/regex/replacement/
替换匹配的文本 sed 's/old/new/' file
d
删除行 sed '3d' file
p
打印行 sed -n '1,5p' file
a\text
在行后追加文本 sed '2a\new line' file
i\text
在行前插入文本 sed '3i\inserted' file
y/abc/ABC/
字符转换(类似tr) sed 'y/a-z/A-Z/' file
2. 示例
假设存在名为 demo.txt
的文件,内容为:
hello world
Hello Universe
start here
mission complete
hallo and hello
goodbye end
基础操作:
# 1. 替换文本(每行第一个匹配)
sed 's/hello/HELLO/' demo.txt
# 输出:
# HELLO world
# Hello Universe
# start here
# mission complete
# hallo and HELLO
# goodbye end# 2. 全局替换(使用g标志)
sed 's/hello/HELLO/g' demo.txt
# 输出:
# HELLO world
# Hello Universe
# start here
# mission complete
# hallo and HELLO
# goodbye end# 3. 删除行(删除第3行)
sed '3d' demo.txt
# 输出:
# hello world
# Hello Universe
# mission complete
# hallo and hello
# goodbye end# 4. 打印特定行(打印2-4行)
sed -n '2,4p' demo.txt
# 输出:
# Hello Universe
# start here
# mission complete
正则表达式操作:
# 1. 替换以"s"开头的行
sed '/^s/ s/here/HERE/' demo.txt
# 输出:第三行 → start HERE# 2. 删除包含"complete"的行
sed '/complete/d' demo.txt
# 输出中无第四行# 3. 在"hallo"行前插入文本
sed '/hallo/i\--INSERT BEFORE--' demo.txt
# 在第五行前插入:--INSERT BEFORE--# 4. 在"goodbye"行后追加文本
sed '/goodbye/a\--APPEND AFTER--' demo.txt
# 在第六行后追加:--APPEND AFTER--
高级操作:
# 1. 使用反向引用(保留部分内容)
echo "2023-01-15" | sed -r 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/'
# 输出:15.01.2023# 2. 仅修改第2-4行
sed '2,4 s/o/O/g' demo.txt
# 输出:
# hello world
# HellO Universe
# start here
# missiOn cOmplete
# hallo and hello
# goodbye end# 3. 多重操作(使用-e)
sed -e 's/hello/HELLO/' -e '3d' demo.txt
# 先替换hello再删除第3行# 4. 字符转换(小写转大写)
sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' demo.txt
# 全转大写
文件操作:
# 1. 修改文件并备份(GNU sed)
sed -i.bak 's/hello/HELLO/g' demo.txt # 生成demo.txt.bak备份# 2. 直接修改文件(无备份)
sed -i 's/hello/HELLO/g' demo.txt
3. 使用技巧
- 测试命令:先用不带
-i
的命令测试,确认无误后再用-i
- 转义特殊字符:对
/
、&
、.
等使用反斜杠转义 - 跨行处理:使用
N
命令读入多行(如sed 'N; s/\n//'
) - 组合命令:用分号分隔多个命令
sed 's/a/A/; 3d' file
⚠️ 警告:使用
-i
时务必先备份,避免不可逆的数据丢失!
三、awk:文本分析
1. 基本语法
-
用途:对文本进行模式扫描和处理,适用于结构化的文本数据(如按列处理)
-
工作模式:逐行扫描文件,对匹配模式的每行执行指定动作(默认动作为打印当前行)
-
核心概念:
- 字段(Field):通过分隔符自动拆分的文本单元(
$1
、$2
...表示) - 记录(Record):默认以换行符分隔的行(可通过
RS
变量修改)
- 字段(Field):通过分隔符自动拆分的文本单元(
-
语法结构:
awk '模式 {动作}' 文件名 # 或使用单行命令 awk -F: '{print $1}' /etc/passwd
-
常用内置变量:
变量 作用 示例 $0
整行内容 awk '{print $0}' file
$n
第n个字段(n=1,2,3...) awk '{print $3}' file
NR
当前行号(记录号) awk '{print NR, $0}' file
NF
当前行的字段数量 awk '{print NF}' file
FS
输入字段分隔符(默认空格) awk 'BEGIN{FS=":"}...'
OFS
输出字段分隔符(默认空格) awk 'BEGIN{OFS="-"}{print $1,$2}'
FILENAME
当前处理文件名 awk '{print FILENAME}' file
-
常用选项:
选项 作用 示例 -F
设置字段分隔符 awk -F',' '{print $2}' data.csv
-v
定义awk变量 awk -v name="Alice" '/name/ {print name}'
-
特殊模式:
模式 作用 BEGIN
处理文本前执行的动作 END
处理完所有文本后执行的动作
2. 示例
假设存在名为 employees.txt
的文件,内容为:
Alice Johnson|28|Engineer|55000
Bob Smith|32|Manager|72000
Carol Davis|25|Designer|48000
基础操作:
# 1. 打印整行
awk '{print $0}' employees.txt# 2. 打印第一列(默认空格分隔,效果不佳)
awk '{print $1}' employees.txt# 3. 设置分隔符为"|",打印姓名
awk -F'|' '{print $1}' employees.txt
# 输出:
# Alice Johnson
# Bob Smith
# Carol Davis# 4. 打印行号和第3列
awk -F'|' '{print NR, $3}' employees.txt
# 输出:
# 1 Engineer
# 2 Manager
# 3 Designer# 5. 打印最后一行(END模式)
awk -F'|' 'END{print "Last employee:", $1}' employees.txt
# 输出:Last employee: Carol Davis
条件过滤:
# 1. 筛选年龄>30的员工
awk -F'|' '$2 > 30 {print $1}' employees.txt
# 输出:Bob Smith# 2. 筛选工程师
awk -F'|' '$3 == "Engineer" {print $1, "($4)"}' employees.txt
# 输出:Alice Johnson (55000)# 3. 使用正则:匹配姓氏含"S"的员工
awk -F'|' '$1 ~ /S/ {print $1}' employees.txt
# 输出:Bob Smith
计算与统计:
# 1. 计算平均工资
awk -F'|' 'NR>1 {sum+=$4; count++} END{print "Avg Salary:", sum/count}' employees.txt
# 输出:Avg Salary: 58333.3# 2. 格式化输出(设置OFS)
awk -F'|' 'BEGIN{OFS=" | "} {print NR, $1, "Salary: $" $4}' employees.txt
# 输出:
# 1 | Alice Johnson | Salary: $55000
# 2 | Bob Smith | Salary: $72000
# 3 | Carol Davis | Salary: $48000# 3. 统计岗位数量(使用数组)
awk -F'|' 'NR>1 {jobs[$3]++} END{for(j in jobs) print j, jobs[j] " person(s)"}' employees.txt
# 输出:
# Engineer 1 person(s)
# Manager 1 person(s)
# Designer 1 person(s)
BEGIN块初始化:
# 添加表头行和美元符号
awk -F'|' 'BEGIN {print "ID | Name | Position | Salary\n--------------------------"} NR>1 {print NR-1, $1, $3, "$" $4}' employees.txt
# 输出:
# ID | Name | Position | Salary
# --------------------------
# 1 Alice Johnson Engineer $55000
# 2 Bob Smith Manager $72000
# 3 Carol Davis Designer $48000
3. 高级技巧
-
内置函数:
# 转换小写 echo "HELLO" | awk '{print tolower($0)}' # → hello# 字符串截取 awk -F'|' '{print substr($1, 1, 3)}' employees.txt # → Ali, Bob, Car
-
条件语句:
# 工资分级 awk -F'|' '{if ($4 > 60000) status="High"else if ($4 > 50000) status="Medium"else status="Low"print $1, ":", status }' employees.txt
-
多文件处理:
# 同时处理两个文件(行号重置) awk '{print FILENAME, NR, "->", $0}' file1.txt file2.txt
-
处理复杂日志:
# 统计Nginx日志中每个IP的访问量 awk '{ip_count[$1]++} END{for(i in ip_count) print i, ip_count[i]}' access.log
4. 常见应用场景
场景 | awk命令 |
---|---|
提取CSV列 | awk -F',' '{print $2,$5}' data.csv |
分析日志 | awk '/ERROR/ {print $1,$5}' app.log |
文本统计 | awk '{chars+=length($0);words+=NF} END{print "Chars:",chars,"Words:",words}' |
数据转换 | awk 'BEGIN{OFS=":"} {$1=$1; print}' file (重新格式化分隔符) |
关联多个文件 | awk 'NR==FNR{a[$1]=$2;next} {print $0, a[$1]}' file1 file2 |
✅ 最佳实践:
- 处理结构化数据优先使用
awk
而非grep/sed
- 复杂任务用
BEGIN
预处理和END
汇总- 避免修改源文件,输出到新文件:
awk '...' input > output
⚠️ 注意:
$0
和$1
等变量不用$
前缀(print $1
正确,print {$1}
错误)- 数值计算需确保字段是数字(非数字会当作0)
四、关于grep与find命令的区别
简单来讲,find主要查找文件,而grep主要查找文件内容
特性 | grep | find |
---|---|---|
目标 | 文件内容 | 文件/目录自身属性 |
主要功能 | 文本模式匹配 | 按条件查找文件/目录 |
递归搜索 | 需 -r 选项(如 grep -r ) | 天生递归(从指定目录开始) |
结合正则 | 默认支持正则表达式 | 仅 -name 支持基础通配符(需用 -regex 支持完整正则) |
find
(文件查找工具)
- 用途:在目录树中查找符合条件(名称、大小、时间等)的文件/目录。
- 工作对象:文件/目录的元数据(如文件名、大小、修改时间)。
- 常用选项:
-name
:按文件名搜索(如find . -name "*.jpg"
)。-type
:按类型搜索(f
=文件,d
=目录)。-size
:按文件大小搜索(如find / -size +100M
找大于100MB的文件)。-mtime
:按修改时间搜索(如find ~ -mtime -7
找7天内修改的文件)。-exec
:对搜索结果执行命令(如find . -name "*.tmp" -exec rm {} \;
)。
- 示例:
# 查找 /home 下所有 .txt 文件 find /home -type f -name "*.txt"# 删除 /tmp 中 30 天前修改的 .log 文件 find /tmp -name "*.log" -mtime +30 -delete
所以在实际使用时,一般先使用find查找到文件,然后使用grep对文件内容进行操作。