文本处理三大利器:grep awk sed

在计算机编程和系统管理中,文本处理是一项非常重要的任务。grep、awk和sed是三种流行的文本处理工具,它们可以让你在命令行中快速处理文本数据。以下是这三种工具的简要介绍:

Unix 哲学

  • 程序应该只关注一个目标,并尽可能把它做好。
  • 让程序能够互相协同工作。
  • 应该让程序处理文本数据流,因为这是一个通用的接口。

grep

grep(Global Regular Expression Print) 主要用来在一个文件中搜索特定字符串。

grep 'test' file.txt # 输出 file.txt 中所有匹配 test 的行

参数列表

参数 说明
-n 显示行号
-v 显示不匹配的行
-o 只显示匹配的内容
-c 显示匹配的行数
-i 忽略大小写
-E 使用扩展的正则表达式,相当于使用 egrep
-P 使用 Perl 正则表达式
-l 在多个文件中操作时,只显示文件名
-x 显示exact匹配的行
-An 显示匹配行的同时,再附加这一行下面的 n 行
-F 不使用正则表达式,只匹配字符串内容
-r 递归搜索子目录

Basic Regular Expressions (BRE) vs Extended Regular Expressions (ERE)

通用的符号:

. ^ $ * [ ]

ERE 符号 (egrep awk emacs):

+ ? ( ) { }

BRE 符号 (grep sed vim):

\+ \? \( \) \{ \} \|

Perl Regular Expressions

Greedy quantifier Lazy quantifier Description
* *? Star Quantifier: 0 or more
+ +? Plus Quantifier: 1 or more
? ?? Optional Quantifier: 0 or 1
{n} {n}? Quantifier: exactly n
{n,} {n,}? Quantifier: n or more
{n,m} {n,m}? Quantifier: between n and m

awk

awk 主要用于文本扫描和处理,名字来源于它的三位作者 Aho, Weinberger & Kernighan (K&R中的K)。

BEGIN { ... initialization awk commands ...}
{ ... awk commands for each line of the file ...}
END { ... finalization awk commands ...}

内置的变量

Variable Meaning
$0 当前行
$1 - $n 第几个字段
FS 输入的列间隔符 默认值:“ “
NF 列数
NR 第几行
FNR 当前文件的第几行
OFS 输出的列间隔符 默认值:“ “
ORS 输出的行间隔符 默认值:“\n”

假设文件 file.txt 的内容如下:

zhang M 1
lee M 2
song F 9
wang F 11

awk '{print $1,$NF,NF}' file.txt

awk 'BEGIN {SUM=0} {SUM=SUM+$3} END { print SUM, SUM/NR}' file.txt

# 假设以逗号为分隔符
zhang , M , 1
lee , M , 2
song , F , 9
wang , F , 11

awk -F, '{print $1,$NF}' file.txt
awk 'BEGIN {FS=","} {print $1,$NF}' file.txt

条件控制

if (condition) statement [ else statement ]
while (condition) statement
do statement while (condition)
for (expr1; expr2; expr3) statement
for (var in array) statement
break
continue
exit [ expression ]

# 计算所有第2列为“M"的行中,第3列数据的总和和平均值
awk 'BEGIN {SUM=0;CNT=0} {if ($2 == "M") {SUM=SUM+$3 ;CNT=CNT+1} } \\
END { print SUM, SUM/CNT}' file.txt

# 计算文件中一共第2列每种类型的个数
awk '{SUM[$2] += 1} END {for (s in SUM) print s,SUM[s]}' file.txt

字符串匹配

awk '{if ($1 == "zhang") print $0}' file.txt # 字符串相等
awk '{if ($1 ~ /ang/) print $0}' file.txt # 匹配正则表达式
awk '{if ($1 !~ /ang/) print $0}' file.txt # 不匹配正则表达式

内置函数

参考 https://www.tutorialspoint.com/awk/awk_built_in_functions.htm

# 使用 printf 格式化输出
awk 'BEGIN {SUM=0;CNT=0} {if ($2 == "M") {SUM=SUM+$3 ;CNT=CNT+1} } \\
END {printf("sum: %d, avg: %.2f\\n", SUM, SUM/CNT)}' file.txt

以脚本形式执行

#!/usr/bin/awk -f

BEGIN {
SUM=0;
CNT=0
}
{
if ($2 == "M") {
SUM=SUM+$3 ;
CNT=CNT+1
}
}
END {
printf("sum: %d, avg: %.2f\\n", SUM, SUM/CNT)
}

chmod a+x ./awk_script
./awk_script file.txt

sed

sed 表示”Stream Editor”,用来对文本或流进行编辑。

假设文件 file.txt 内容如下:

zhang,zhang,M,123
zhang,lee,M,124
lee,san,F,10
wang,wu,M,23

删除某行:d 命令

sed '/zhang/d' file.txt # 删除匹配的行
sed '/zhang.*lee/d' file.txt # 删除匹配的行
sed '/zhang.*lee/!d' file.txt # 删除不匹配的行
sed '2d' file.txt # 删除第2行
sed '1,2d' file.txt # 删除第1至第2行
sed '2,$d' file.txt # 删除第2行至最末

文本替换:s 命令

sed 's/zhang/angy/' file.txt # 将所有行第1个 zhang 替换为 angy
sed 's/zhang/angy/g' file.txt # 将所有行所有的 zhang 替换为 angy
sed 's/zhang/angy/2' file.txt # 将所有行第2个 zhang 替换为 angy
sed 's/zhang/angy/2g' file.txt # 将所有行第2个至最后一个 zhang 替换为 angy
sed '2,3s/zhang/angy/g' file.txt # 将第2行至第3行所有的 zhang 替换为 angy
sed '3,$s/zhang/angy/g' file.txt # 将第3行至最后一行所有的 zhang 替换为 angy

插入和追加一行: i & a 命令

sed '2 i test' file.txt # 在第2行之前插入新行,内容为 test
sed '2 a test' file.txt # 在第2后之后追加新行,内容为 test
sed '/zhang/a append line' file.txt # 在所有包含 zhang 的行的后面追加新行

替换整行:c 命令

sed '2 c test' file.txt # 将第2行的内容替换为 test

# 将所有包含 zhang 的行替换为 changed line
sed '/zhang/c changed line' file.txt

修改原始文件

# 直接将 file.txt 中 所有行的第1个 zhang 替换为 angy
sed -i 's/zhang/angy/' file.txt
# 在 file.txt 中完成替换,并将原始文件另存为 file.txt.bak
sed -i.bak 's/zhang/angy/' file.txt

综合使用

cat file.txt | sed '/lee/d' | awk -F, '{print $1,$2}'

zhang zhang
wang wu