Skip to content

正则表达式

分类

工具默认支持的正则类型扩展正则启用方法
grep基本正则(BRE)grep -Eegrep
sed基本正则(BRE)sed -E(GNU)或 sed -r
awk扩展正则(ERE)默认支持,无需额外选项

基本元字符

元字符作用示例
^匹配行首^admin
$匹配行尾error$
^$匹配空行^$
.匹配任意单个字符a.c → abc, a1c
*匹配前一个字符0次到无限次go*d → gd, good
\转义符\.,\*,\\
+匹配前一个字符1次到无限次go+d → god, gooood
?匹配前一个字符 0或1 次colou?r → color/colour
[]匹配中括号中的任意一个字符[aeiou] → 匹配元音字母
[^]取反,不匹配中括号内的字符[^0-9] → 非数字字符
^[]以中括号内任意字符开头^[abc]
``逻辑或(扩展正则)
.*表示任意.*
.*?转为懒惰(尽可能少),匹配到第一个满足条件.*?
{n}精确匹配 n 次(扩展正则)a{3} → aaa
{n,}至少匹配 n 次(扩展正则)a{2,} → aa, aaa…
{,n}匹配前一个字符0次到n次(扩展正则)
{n,m}匹配前一个字符n次到m次(扩展正则)a{2,4} → aa, aaa, aaaa
()用于分组,可以将多个字符作为一个整体进行匹配(abc)

转义字符

字符功能描述示例数据
\.匹配字符 .\.
\*匹配字符 *\*
\+匹配字符 +\+
\?匹配字符 ?\?
$匹配字符 ($
$匹配字符 )$
$$匹配字符 [$$
$$匹配字符 ]$$
\{匹配字符 {\{
\}匹配字符 }\}
\^匹配字符 ^\^
\$匹配字符 $\$
``匹配字符 `

预定义字符类

字符功能描述适用场景
\d匹配任意一个数字字符匹配任意一个数字,如 019 等。
\D匹配任意一个非数字字符匹配任意一个非数字字符,如 ab_ 等。
\w匹配任意一个字母、数字或下划线字符匹配任意一个字母、数字或下划线,如 a1_ 等。
\W匹配任意一个非字母、数字或下划线字符匹配任意一个非字母、数字或下划线字符,如 !@# 等。
\s匹配任意一个空白字符匹配任意一个空白字符,如空格、制表符、换行符等。
\S匹配任意一个非空白字符匹配任意一个非空白字符,如 a1_ 等。

不同工具支持对比

工具支持预定义字符启用方式
grep-P模式grep -P '\d'
sed不支持需用[0-9]替代
awk部分支持\s \S可用
perl完全支持原生支持

贪婪与懒惰匹配

字符功能描述示例数据适用场景
*?懒惰匹配前面的字符零次或多次a*?懒惰匹配前面的字符零次或多次,如 a*? 匹配 a 时只匹配一个 a
+?懒惰匹配前面的字符一次或多次a+?懒惰匹配前面的字符一次或多次,如 a+? 匹配 aa 时只匹配一个 a
??懒惰匹配前面的字符零次或一次a??懒惰匹配前面的字符零次或一次,如 a?? 匹配 aa 时只匹配一个 a
{n,}?懒惰匹配前面的字符至少 na{2,}?懒惰匹配前面的字符至少 n 次,如 a{2,}? 匹配 aaa 时只匹配 aa
{n,m}?懒惰匹配前面的字符至少 n 次,至多 ma{2,4}?懒惰匹配前面的字符至少 n 次,至多 m 次,如 a{2,4}? 匹配 aaaa 时只匹配 aa

工具差异与注意事项

工具/语法转义规则特点示例对比
BRE(基础正则)需转义 +?、`{}` 等扩展元字符
ERE(扩展正则)直接支持 +?、``,无需转义
PCRE(Perl兼容)支持零宽断言、命名分组等高级功能grep -P '\d+(?=元)' → 匹配数字后接 "元"
Shell 引号处理单引号 '' 避免变量扩展,双引号 "" 需二次转义grep "\\bword\\b" → 双引号需转义 \

grep - 文本搜索

快速过滤文本行,支持正则表达式匹配。

语法格式

bash
grep [选项] 模式 [文件/目录...]

选项

选项作用示例
-i忽略大小写grep -i "error" log.txt → 搜索包含 error/Error/ERROR 的行
-v反向匹配(排除匹配行)grep -v "success" data.csv → 显示不包含 "success" 的行
-n显示匹配行的行号grep -n "timeout" app.log → 输出格式:行号:匹配内容
-r递归搜索目录下的所有文件grep -r "192.168.1.1" /etc/ → 在 /etc/ 目录所有文件中搜索 IP
-h搜索多文件时不显示文件名grep -h "timeout" app.log
-H搜索多文件时显示文件名grep -H "timeout" app.log
-c统计匹配行数(而非显示内容)grep -c "404" access.log → 返回 404 错误的出现次数
-l仅显示包含匹配项的文件名grep -l "panic" *.log → 列出所有包含 "panic" 的日志文件名
-w全词匹配(避免部分匹配)grep -w "port" config.conf → 只匹配 "port",排除 "export" 等部分匹配
-A NUM显示匹配行及后续 NUM 行grep -A 3 "Exception" app.log → 显示异常行及其后 3 行(上下文分析)
-B NUM显示匹配行及前 NUM 行grep -B 2 "Segmentation fault" crash.log → 查看崩溃前的 2 行日志
-C NUM显示匹配行前后各 NUM 行grep -C 2 "deadlock" system.log → 显示死锁行附近 2 行上下文
-E启用扩展正则(ERE)grep -E "error"
-o仅输出匹配部分grep -Eo "[0-9]+\.[0-9]+\.[0-9]+" data

经典案例

bash
# 搜索包含 "error" 或 "404" 的行,并显示前后2行
grep -C 2 -E "error|404" access.log
# 统计所有 .log 文件中 "timeout" 出现的次数
grep -rch "timeout" /var/log/*.log
# 提取所有 IPv4 地址
grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}" data.txt
# 统计 Nginx 日志中 5xx 错误的数量
grep -E " 5[0-9]{2} " access.log | awk '{print $9}' | sort | uniq -c
# 查找包含 "error" 但不包含 "warning" 的行
grep "error" file.log | grep -v "warning"

sed - 流式文本编辑

对文本进行流式编辑(替换、删除、插入等),支持正则表达式。sed 工作原理是从文件中逐行读取内容。

语法格式

bash
sed [选项] '地址范围/操作/参数' 文件
sed [选项] '脚本命令' 输入文件
sed [选项] -f 脚本文件 输入文件

选项

选项描述示例
-e script执行多条指令sed -e 's/foo/bar/' -e 's/abc/123/' file
-f file从文件读取脚本命令sed -f script.sed file
-i[后缀]直接修改文件(可选备份后缀)sed -i.bak 's/foo/bar/' file
-n禁止默认输出sed -n '1,5p' file
-r使用扩展正则表达式sed -r 's/[0-9]+/NUM/g' file.txt
-l指定行长度(与 l 命令配合显示行内容)sed -n 'l 50' file.txt (每行最多显示 50 个字符)
-s将多个输入文件视为独立文本流处理sed -s 's/foo/bar/' file1.txt file2.txt

操作

命令解释说明示例
a追加,在指定行后添加一行或多行文本*sed '2a\This is appended text' file.txt
c替换指定行的内容sed '3c\This replaces line 3' file.txt
d删除指定的行*sed '/pattern/d' file.txt
i插入,在指定行前添加一行或多行文本*sed '1i\This is inserted text' file.txt
H将指定行内容复制sed '1{H;d}; 2G' file.txt
g把保持空间的内容复制到模式空间sed '1h; 2g' file.txt
G将复制的内容粘贴到指定行sed '1h; 2G' file.txt
l打印不可见的字符sed -n 'l' file.txt
n清空模式空间的内容并读入下一行sed 'n;d' file.txt
p打印内容,通常与-n选项一起使用*sed -n '/pattern/p' file.txt
P打印模式空间的内容,直到遇到换行符\n结束操作sed -n 'N;P' file.txt
r从指定文件读取数据sed '/pattern/r otherfile.txt' file.txt
s替换文本s#old#new#gg是替换标志,不是命令)*sed 's/foo/bar/g' file.txt
w另存,把模式空间的内容保存到文件中sed -n '/pattern/w output.txt' file.txt
y根据对应位置转换字符sed 'y/abc/ABC/' file.txt
:label定义一个标签sed ':start; s/A/B/; t start' file.txt
b label执行该标签后面的命令sed '/pattern/b end; s/A/B/; :end' file.txt
t如果前面的命令执行成功,跳转到指定标签sed 's/A/B/; t done; s/C/D/; :done' file.txt

注意

  • 在匹配字符场景下操作aic需要后面加\

    bash
    sed '/user/a\这是匹配字符下一行追加' filename
    sed '/user/i\这是匹配字符上一行追加' filename
    sed '/user/c\这是匹配字符行替换' filename

特殊符号

符号解释说明示例
!对指定行以外的所有行应用命令*sed '1,3!d' file.txt(保留1-3行,删除其余行)
=打印当前行行号sed '=' file.txt(输出每行内容前先打印行号)
~步长选择First~Step 表示从 First 行开始,每隔 Step 行处理一次sed '1~2d' file.txt(删除所有奇数行)
&代表被替换的内容(在 s 命令中使用)sed 's/[0-9]/[&]/g' file.txt(把所有数字用 [] 包裹)
:定义标签,用于 b(跳转)和 t(条件跳转)命令*sed ':loop; s/foo/bar/; t loop' file.txt(循环替换直到没有匹配)
{}对单个地址或地址范围执行批量操作sed '/pattern/{s/foo/bar/; p}' file.txt(匹配 pattern 的行先替换再打印)
+地址范围加法运算(GNU sed 支持)sed '1,+2d' file.txt(删除第1行及接下来的2行)

分组

在 sed 中,分组是通过()实现的,用于:

  1. 将多个字符视为一个整体
  2. 在替换时引用匹配的内容
  3. 实现复杂的模式匹配

基本语法

bash
# 基本分组语法 -r支持扩展正则
sed -r 's/(pattern)/\1/' filename

# 引用分组
\0	# 所有
\1  # 第一个分组
\2  # 第二个分组
...
\9  # 第九个分组

# 给数字加括号
echo "abc123def" | sed 's/([0-9]+)/(\1)/g'
# 输出: abc(123)def
# 格式化日期 (YYYYMMDD → YYYY-MM-DD)
echo "20231225" | sed -r 's/([0-9]{4})([0-9]{2})([0-9]{2})/\1-\2-\3/'
# 输出: 2023-12-25


# 批量文件重命名
[root@localhost demo]# ls
test-2025-05-22-abc-10.jpg  test-2025-05-22-abc-2.jpg  test-2025-05-22-abc-4.jpg  test-2025-05-22-abc-6.jpg  test-2025-05-22-abc-8.jpg
test-2025-05-22-abc-1.jpg   test-2025-05-22-abc-3.jpg  test-2025-05-22-abc-5.jpg  test-2025-05-22-abc-7.jpg  test-2025-05-22-abc-9.jpg
[root@localhost demo]# ls | sed -r 's/([a-z]{4})-([0-9]{4})-([0-9]{2})-([0-9]{2})-([a-z]{3})-([0-9]{1,3})(.jpg)/mv \0 \1_\5_\6\7/'
mv test-2025-05-22-abc-10.jpg test_abc_10.jpg
mv test-2025-05-22-abc-1.jpg test_abc_1.jpg
mv test-2025-05-22-abc-2.jpg test_abc_2.jpg
mv test-2025-05-22-abc-3.jpg test_abc_3.jpg
mv test-2025-05-22-abc-4.jpg test_abc_4.jpg
mv test-2025-05-22-abc-5.jpg test_abc_5.jpg
mv test-2025-05-22-abc-6.jpg test_abc_6.jpg
mv test-2025-05-22-abc-7.jpg test_abc_7.jpg
mv test-2025-05-22-abc-8.jpg test_abc_8.jpg
mv test-2025-05-22-abc-9.jpg test_abc_9.jpg

经典案例

打印匹配行

bash
sed -n '80,90p' app.log           	# 打印80到90行内容
sed -n '80p;90p' app.log          	# 打印80和90行内容
sed -n '/error/p' app.log         	# 打印匹配"error"行,不显示其它内容
sed -n '20,/error/p' app.log      	# 打印第20行到匹配的"error"行的内容
sed -n '20p;/error/p' app.log     	# 打印第20行和匹配的"error"行的内容
sed -n '/stop$/p' app.log			# 打印以 "stop" 结尾的内容
sed -n '/INFO/p;/WARN/p' app.log	# 打印匹配 "INFO" 和 "WARN" 行的内容

# 匹配ifconfig的第二行并匹配IP地址\1是打印第1个分组()	的值,\0打印所有
ifconfig | sed -n '2p' | sed -r 's/inet (.*) netmask (.*) broadcast (.*)/\1/'

替换匹配行内容

bash
sed 's/2023/2025/' app.log 			# 每行首个 "2023" 替换 "2025",结果打印终端,源数据未修改
sed 's/23/25/g' app.log				# 所有 "23" 替换 "25"(全局替换),结果打印终端,源数据未修改
sed 's/apple/orange/g' app.log		# 所有 "apple" 替换 "orange"(全局替换),结果打印终端上,源数据未修改
sed -i 's/2023/2025/' app.log		# 每行首个 "2023" 替换 "2025",修改源文件内容,无结果输出
sed -i.bak 's/admin/demo/g' *.log  # 批量替换所有日志中的 "admin" 为 "demo" 并备份

删除匹配内容

bash
sed -e '/^$/d' -e '/^#/d' config.conf	# 删除空行和#开头行内容。(^$表示空行)
sed '/stop$/d' app.log					# 删除以 "stop" 结尾的内容 
sed '/error/d' app.log            		# 删除匹配 "error" 行余内容 
sed '/ERROR/d;/FATAL/d' app.log			# 删除匹配 "ERROR" 和 "FATAL" 行内容

在匹配行插入/追加文本

bash
sed 'x,yi aaaaaaaaaa' 						# 在x行到y行前增加内容(也可按字符操作sed '/字符1/,/字符2/i  内容')
sed 'x,ya aaaaaaaaaa' 						# 在x行到y行后增加内容
sed '$ aaaaaaaaaa' 							# 在最后一行增加内容
sed 'x,ys#^#内容#'							# 把文件x行到y行的行首内容替换
sed 'x,ys#$#内容#'							# 把文件x行到y行的行尾内容替换
sed 's/user root/#&/' config.conf 			# 在匹配内容前插入,在同一行内
sed 's/user root/&#/' config.conf 			# 在匹配内容后插入,在同一行内
sed '/user root/i\# 这是注释' config.conf	  # 在匹配 "user root" 行的上一行插入 "# 这是注释" ,未修改源数据
sed '/pattern/a\ADDED_TEXT' config.conf		# 在匹配 "pattern" 的下一行追加 "ADDED_TEXT",未修改源数据

直接修改原文件(危险操作!)

bash
sed -i.bak 's/old/new/g' config.conf  # 先备份原文件

正则

在 JSON 文件中的 "port": 值后插入 8080
bash
# config.json
{
  "service": {
    "port": 80,
    "host": "localhost"
  }
}
sed -E 's/("port": )[0-9]+/&80/' config.json	# -E开启正则匹配

注意事项

  1. -i 选项会直接修改源文件,重要文件操作前建议先测试或备份
  2. 特殊字符(如 / &)在替换模式中需要转义
  3. 默认输出所有处理后的行,使用 -n 配合 p 命令可控制输出
  4. 不同版本sed存在差异(如MacOS使用BSD版本,Linux使用GNU版本)
  5. 复杂文本处理建议结合awk或其他工具使用

awk - 结构化文本处理

awk 是一种强大的文本处理工具,主要用于模式扫描和处理语言。它特别适合处理结构化文本数据,如日志文件、CSV文件等。awk 可以执行复杂的文本分析、数据提取和报告生成任务。

语法格式

bash
awk '模式 {动作}' 文件
awk [选项] '脚本命令' 输入文件                  # 直接执行脚本
awk [选项] -f 脚本文件 输入文件                # 从文件读取脚本

脚本结构

bash
awk 'BEGIN{预处理} /模式/{动作} END{收尾处理}' 文件名
  • BEGIN 块:处理输入数据前执行,用于初始化变量或打印表头(如 BEGIN{FS=","} 设置分隔符)
  • 模式+动作块:逐行处理数据,匹配模式(正则/条件)时执行动作(如 $3 > 50 {print}
  • END 块:处理完所有数据后执行,常用于输出统计结果(如 END{print "总和:", sum}

选项

选项功能说明典型用法示例
-F fs指定字段分隔符(支持正则表达式)awk -F':' '{print $1}' 按冒号分列
-v var=value定义外部变量传递给 awkawk -v OFS="," '{print $1,$3}' 设置输出分隔符1****9
-f script从脚本文件读取命令awk -f script.awk data.txt 执行复杂脚本
!~不匹配运算符awk '$1 !~ "nobody"' file.txt
-m设置字段/记录数限制awk -mf 1000 限制最大字段数1
--debug调试模式显示处理过程awk --debug '/error/{print}' log.txt
-n识别八进制/十六进制数据awk -n '{print $1+1}' 处理特殊进制数值

数组

AWK 的数组是非常强大的数据结构,它实际上是关联数组(类似于其他语言的字典或哈希表)。下面我将从基础到高级全面讲解 AWK 数组的使用方法。

数组定义与赋值

bash
# 定义数组并赋值
arr["name"] = "Linux"
arr["version"] = 5.15
arr[1] = "first"
arr[2] = "second"

# 访问数组元素
print arr["name"]    # 输出: Linux
print arr[2]         # 输出: second

# 检查元素是否存在
if ("version" in arr) {
    print "版本存在:", arr["version"]
}

数组遍历

bash
# 基本遍历方法
for (key in arr) {
    print key " => " arr[key]
}

# 控制遍历顺序 (GNU awk 特有)
# 按索引字符串排序
PROCINFO["sorted_in"] = "@ind_str_asc"
for (i in arr) { print i, arr[i] }

# 按索引数值排序
PROCINFO["sorted_in"] = "@ind_num_asc"
for (i in arr) { print i, arr[i] }

数组操作

bash
delete arr["version"]  	# 删除单个元素
delete arr             	# 删除整个数组
print length(arr)  		# 获取数组元素个数

# 多维数组模拟
# AWK 不支持真正的多维数组,但可以通过 SUBSEP 分隔符模拟:
multi["row1", "col1"] = "data1"
multi["row1", "col2"] = "data2"

for (k in multi) {
    split(k, sep, SUBSEP)
    print sep[1], sep[2], multi[k]
}

内置变量

变量名描述数据类型示例
$0所有列的数据列表awk '{print $0}' file
$1-$9第1-9列的数据字符串awk 'print $1,$2' file
NR当前行号数字awk '{print NR, $0}' file
NF当前列的总数数字awk '{print $NF}' file
FS输入列分隔符字符串awk 'BEGIN{FS=":"} {print $1}'
OFS输出列分隔符字符串awk 'BEGIN{OFS=";"} {$1=$1; print}'
RS输入行分隔符字符串awk 'BEGIN{RS=";"} {print}'
ORS输出行分隔符字符串awk 'BEGIN{ORS="\n\n"} {print}'
FILENAME当前输入文件名字符串awk '{print FILENAME, NR, $0}' file
FNR当前文件中的记录号数字awk '{print FNR, $0}' file1 file2
ARGC命令行参数个数数字awk 'BEGIN{print ARGC}'
ARGV命令行参数数组数组awk 'BEGIN{print ARGV[1]}' file
ENVIRON环境变量数组数组awk 'BEGIN{print ENVIRON["HOME"]}'
CONVFMT数字转换格式字符串awk 'BEGIN{CONVFMT="%d"; print 12.345}'
OFMT数字输出格式字符串awk 'BEGIN{OFMT="%.2f"; print 12.345}'
SUBSEP多维数组下标分隔符字符串awk 'BEGIN{SUBSEP=":"; a[1,2]=3}'

经典案例

按行操作与匹配

bash
awk 'NR>5' app.log 					# 打印从第6行到末尾
awk 'NR>=5 && NR<=8' app.log		 # 打印第5行到第8行
awk 'NR<=5 || NR>=8' app.log		 # 打印第1到5行和第8行到末尾
awk '/INFO/' app.log				# 打印所有匹配 "INFO" 行内容 
awk '/INFO/;/WARN/' app.log			 # 打印所有匹配 "INFO" 和 "WARN" 行内容

选项案例

bash
# 使用冒号作为分隔符处理/etc/passwd
awk -F: '{print $1, $7}' /etc/passwd
# 使用多个字符作为分隔符
awk -F'[:/]' '{print $1, $4}' /etc/passwd
# 使用正则表达式作为分隔符(分隔数字和非数字)
awk -F'[0-9]+' '{print $1, $2}' data.txt
# 定义搜索阈值变量
awk -v threshold=100 '$3 > threshold' data.txt
# 定义多个变量
awk -v name="John" -v age=30 'BEGIN{print name, age}'
# 使用系统变量
awk -v user="$USER" 'BEGIN{print "Current user:", user}'
# 从脚本文件执行
awk -f process.awk data.txt
# 组合多个脚本文件
awk -f header.awk -f body.awk -f footer.awk input.txt
# 组合使用-F和-v选项
awk -F: -v OFS="\t" '{print $1, $3, $6}' /etc/passwd
# 同时使用脚本文件和命令行程序
awk -f header.awk -v date="$(date)" 'END{print "Processed on:", date}' data.txt
# 处理多文件时使用不同分隔符
awk -F: '{print FILENAME, $1}' /etc/passwd -F, '{print FILENAME, $2}' data.csv
注意事项
  1. 选项顺序很重要,后面的选项可能覆盖前面的设置
  2. -F选项会影响所有后续的文件处理,除非被新的-F覆盖
  3. -v定义的变量在BEGIN块中即可使用
  4. 使用--可以确保后续参数不被解释为选项
  5. 不同版本的awk(GNU awk、mawk等)支持的选项可能有差异
  6. 复杂脚本建议使用-f选项从文件加载,提高可维护性

预定义变量案例

bash
# FS - 输入列分隔符(等效于-F选项)
awk 'BEGIN{FS=","} {print $1, $3}' data.csv
# OFS - 输出列分隔符(格式化输出)
awk 'BEGIN{OFS=" | "} {$1=$1; print}' data.txt
# RS - 自定义行分隔符(处理段落)
awk 'BEGIN{RS=""; FS="\n"} {print "段落", NR, "有", NF, "行"}' doc.txt
# ORS - 输出行分隔符(改变换行符)
awk 'BEGIN{ORS="\n\n"} {print $0}' items.txt
# OFMT - 数字输出格式
awk 'BEGIN{OFMT="%.2f"} {sum+=$3} END{print "总和:", sum}' sales.txt
# CONVFMT - 数字转换格式
awk 'BEGIN{CONVFMT="%d"} {print $1/3}' numbers.txt
# NR - 总记录数统计
awk 'END{print "Total records:", NR}' access.log
# FNR - 文件内记录数(处理多文件时特别有用)
awk '{print FILENAME, FNR, $1}' file1.txt file2.txt
# NF - 字段数量检查
awk 'NF != 6 {print "Line", NR, "has", NF, "fields"}' data.csv
注意事项
  1. 内置变量名区分大小写(全部大写)
  2. 修改$0或字段后会重新计算NF
  3. NR对所有文件累计计数,FNR对每个文件单独计数
  4. ENVIRON数组访问的环境变量名需大写
  5. 某些变量(如PROCINFO)是GNU awk特有
  6. 在BEGIN块中修改变量最安全(如FSOFS等)
  7. SUBSEP默认值是\034(不可见字符)

数组案例

基本练习

bash
cat >> domain.log << EOF
http://www.yunjisuan.org/index.html 10
http://www.yunjisuan.org/index.html 10
http://post.yunjisuan.org/index.html 15
http://mp3.yunjisuan.org/index.html 50
http://www.yunjisuan.org/index.html 10
http://post.yunjisuan.org/index.html 15
EOF


# 统计每个域名请求次数和域名总的请求流量
awk -F "[/. ]+" '{
	DOMAIN[$2]+=1;
	TOTAL[$2]+=$NF
} 
END{
	for (d in DOMAIN) { 
		print "域名:" d "\t请求数:" DOMAIN[d] "\t流量:" TOTAL[d]
    }
}' domain.log 

# 或者
awk -F "[/. ]+" '{
 DOMAIN[$2]["count"]+=1;
 DOMAIN[$2]["total"]+=$NF
} 
END{
 for (i in DOMAIN) {
    print "域名:"i "\t请求数:"DOMAIN[i]["count"] "\t流量:"DOMAIN[i]["total"]
 }
}' domain.log

Nginx日志统计

bash
# 格式如下
101.226.61.184 - - [22/Nov/2015:11:02:00 +0800] "GET /mobile/sea-modules/gallery/zepto/1.1.3/zepto.js HTTP/1.1" 200 24662 "http://m.oldboyedu.com.cn/mobile/theme/old/home/index.html" "Mozilla/5.0 (Linux; U; Android 5.1.1; zh-cn; HUAWEI CRR-UL00 Build/HUAWEICRR-UL00) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/5.4 TBS/025478 Mobile Safari/533.1 MicroMessenger/6.3.7.51_rbb7fa12.660 NetType/3gnet Language/zh_CN"


# 统计每个ip的请求次数、流量、每个状态码的次数
awk '
{                         
    DOMAIN[$1]["ip"]++;
    DOMAIN[$1]["code"][$9]++;
    DOMAIN[$1]["kbs"]+=$10
}
END{
	# 输出表头(%-20s表示向左对齐宽度20)
    printf "│%-20s│%-10s│%-15s│%-20s│\n", "IP", "请求数", "流量(MB)", "状态码"
    for (i in DOMAIN){
        ## 保留小数点后两位
        kbs=sprintf("%.2f", DOMAIN[i]["kbs"]/1024/1024)
	    codes = ""
	    # 拼接状态码为字符串
	    for (c in DOMAIN[i]["code"]) {
	    	codes = codes sprintf("%s:%d ", c, DOMAIN[i]["code"][c])
	    }
	    printf "│%-20s│%-10s│%-15s│%-20s│\n",i,DOMAIN[i]["ip"],kbs,codes
    }
}' access.log | column -t

# column -t 对齐

注意事项

  1. awk 默认使用空格和制表符作为字段分隔符
  2. 内置变量如 NR(行号)、NF(字段数)、FS(字段分隔符)等需要大写
  3. 在 BEGIN 块中执行的代码在处理任何输入前运行
  4. 在 END 块中执行的代码在处理完所有输入后运行
  5. awk 支持正则表达式匹配,可以使用 ~ 和 !~ 运算符
  6. 复杂的awk脚本建议保存到单独文件中使用-f选项调用
  7. 不同版本的awk(GNU awk、nawk等)可能有细微差异

实际应用场景

  • grep 命令:用于搜索文件中的文本。例如,grep '^abc' file.txt 会搜索 file.txt 中所有以 abc 开头的行。
  • sed 命令:用于文本替换。例如,sed 's/a/b/g' file.txt 会将 file.txt 中所有的 a 替换为 b
  • awk 命令:用于文本处理。例如,awk '/^abc/ {print $0}' file.txt 会打印 file.txt 中所有以 abc 开头的行。