grep与正则表达式
grep基础
基本语法
grep [选项] 模式 [文件...]
grep [选项] [-e 模式 | -f 模式文件] [文件...]
常用选项
选项 说明 -i忽略大小写 -v反向匹配(显示不匹配的行) -n显示行号 -c只显示匹配行数 -l只显示匹配的文件名 -L只显示不匹配的文件名 -w匹配整个单词 -x匹配整行 -o只显示匹配的部分 -r / -R递归搜索目录 -E使用扩展正则表达式 -F固定字符串匹配(不解释正则) -P使用Perl正则表达式 -A n显示匹配行及后n行 -B n显示匹配行及前n行 -C n显示匹配行及前后各n行 --color高亮显示匹配内容
基本用法示例
# 基本搜索
grep "error" /var/log/syslog
# 忽略大小写
grep -i "error" logfile
# 显示行号
grep -n "pattern" file
# 递归搜索目录
grep -r "function" /path/to/project/
# 只显示匹配的文件名
grep -l "TODO" * .py
# 显示匹配行数
grep -c "error" logfile
# 反向匹配
grep -v "^#" config.conf # 排除注释行
# 匹配整个单词
grep -w "cat" file # 不匹配category
# 只显示匹配部分
grep -o "[0-9]\+" file # 只显示数字
# 上下文显示
grep -C 3 "error" logfile # 显示匹配行及前后3行
正则表达式
基本正则表达式(BRE)
元字符 说明 .匹配任意单个字符 *匹配前一个字符0次或多次 [abc]匹配a、b或c中的任意一个 [^abc]匹配除a、b、c外的任意字符 [a-z]匹配a到z范围内的字符 ^行首 $行尾 \{n,m\}匹配前一个字符n到m次 \{n\}匹配前一个字符n次 \{n,\}匹配前一个字符至少n次 \(\)分组 \1 \2引用第1、2个分组
扩展正则表达式(ERE)
使用 grep -E 或 egrep
元字符 说明 +匹配前一个字符1次或多次 ?匹配前一个字符0次或1次 ` ` ()分组 {n,m}匹配前一个字符n到m次
Perl正则表达式(PCRE)
使用 grep -P
元字符 说明 \d数字 [0-9] \D非数字 \s空白字符 \S非空白字符 \w单词字符 [a-zA-Z0-9_] \W非单词字符 \b单词边界 \B非单词边界
常用正则模式
匹配IP地址
# 简单匹配
grep -E "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" file
# 更精确匹配
grep -P "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" file
匹配邮箱
grep -E "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" file
匹配URL
grep -E "https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}[/a-zA-Z0-9._?=&-]*" file
匹配日期
# YYYY-MM-DD
grep -E "[0-9]{4}-[0-9]{2}-[0-9]{2}" file
# DD/MM/YYYY
grep -E "[0-9]{2}/[0-9]{2}/[0-9]{4}" file
匹配手机号
# 中国手机号
grep -E "1[3-9][0-9]{9}" file
匹配空行
grep "^$" file
grep -v "^$" file # 排除空行
匹配注释行
grep "^#" file # #开头的注释
grep "^[[:space:]]*#" file # 允许前面有空格
grep -v "^#" file # 排除注释行
高级用法
多模式匹配
# 使用-e指定多个模式
grep -e "error" -e "warning" -e "fail" logfile
# 使用扩展正则的|
grep -E "error|warning|fail" logfile
# 从文件读取模式
grep -f patterns.txt file
排除文件和目录
# 排除特定文件
grep -r --exclude= "*.log" "pattern" /path/
# 排除特定目录
grep -r --exclude-dir= ".git" "pattern" /path/
# 排除多个
grep -r --exclude= "*.log" --exclude= "*.tmp" --exclude-dir= ".git" "pattern" /path/
统计分析
# 统计各IP访问次数
grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" access.log | sort | uniq -c | sort -rn
# 统计HTTP状态码
grep -o " \" [0-9]\{3\} " access.log | grep -o "[0-9]\{3\}" | sort | uniq -c
# 统计匹配次数
grep -c "error" logfile
配合其他命令
# 管道过滤
ps aux | grep nginx
ps aux | grep "[n]ginx" # 排除grep进程本身
# 查找文件内容并显示文件名
find . -name "*.py" -exec grep -l "import requests" {} \;
# 结合xargs
grep -rl "old_string" . | xargs sed -i 's/old_string/new_string/g'
# 统计代码行数(排除空行和注释)
grep -v "^#" file | grep -v "^$" | wc -l
grep变体
egrep
# 等同于 grep -E
egrep "pattern1|pattern2" file
fgrep
# 等同于 grep -F,不解释正则
fgrep "string" file
rgrep
# 等同于 grep -r
rgrep "pattern" /path/
zgrep
# 搜索压缩文件
zgrep "pattern" file.gz
性能优化
# 使用-F加速固定字符串搜索
grep -F "exact_string" large_file
# 使用--mmap(某些系统)
grep --mmap "pattern" large_file
# 限制匹配次数
grep -m 10 "pattern" file # 找到10个匹配后停止
# 并行搜索(使用parallel)
find . -type f | parallel grep -H "pattern" {}
💡 grep技巧 :
使用 -F 加速固定字符串搜索
使用 grep "[p]attern" 避免匹配grep进程
复杂正则先用 --color 测试
大文件考虑使用 ripgrep (rg) 替代
🔗 相关笔记 : 04.02_sed与awk 02.06_Shell脚本 99.01_常用命令速查表
sed与awk文本处理
sed流编辑器
基本语法
sed [选项] '命令' 文件
sed [选项] -f 脚本文件 文件
常用选项
选项 说明 -n抑制自动输出,只输出处理的行 -e指定多个编辑命令 -f从脚本文件读取命令 -i直接修改文件(慎用) -i.bak修改前备份原文件 -r / -E使用扩展正则表达式
地址定界
# 行号
sed '3d' file # 删除第3行
sed '1,5d' file # 删除1-5行
sed '$d' file # 删除最后一行
# 步长
sed '1~2d' file # 删除奇数行(从第1行开始,步长2)
sed '2~2d' file # 删除偶数行
# 正则匹配
sed '/pattern/d' file # 删除匹配行
sed '/start/,/end/d' file # 删除start到end之间的行
sed '/pattern/!d' file # 删除不匹配的行
替换命令
# 基本替换
sed 's/old/new/' file # 替换每行第一个
sed 's/old/new/g' file # 替换每行所有
sed 's/old/new/2' file # 替换每行第2个
sed 's/old/new/gp' file # 替换并打印修改的行
sed 's/old/new/gw output' file # 替换并写入output
# 忽略大小写
sed 's/old/new/gi' file
# 使用分隔符
sed 's|/path/old|/path/new|g' file # 使用|作为分隔符
sed 's#/old#/new#g' file # 使用#作为分隔符
# 使用正则
sed -r 's/[0-9]+/NUMBER/g' file # 数字替换为NUMBER
sed 's/\b[a-z]\+\b/WORD/g' file # 单词替换
# 使用捕获组
sed -r 's/([a-z]+) ([0-9]+)/\2 \1/g' file # 交换位置
# 直接修改文件
sed -i 's/old/new/g' file
sed -i.bak 's/old/new/g' file # 修改前备份
删除命令
sed 'd' file # 删除所有行
sed '3d' file # 删除第3行
sed '/pattern/d' file # 删除匹配行
sed '/^$/d' file # 删除空行
sed '/^#/d' file # 删除注释行
插入和追加
# i - 在匹配行前插入
sed '3i\new line' file # 在第3行前插入
sed '/pattern/i\new line' file # 在匹配行前插入
# a - 在匹配行后追加
sed '3a\new line' file # 在第3行后追加
sed '/pattern/a\new line' file # 在匹配行后追加
# c - 替换整行
sed '3c\new line' file # 替换第3行
sed '/pattern/c\new line' file # 替换匹配行
# 多行插入
sed '3i\
line1\
line2\
line3' file
打印命令
sed -n '3p' file # 打印第3行
sed -n '1,5p' file # 打印1-5行
sed -n '/pattern/p' file # 打印匹配行
sed -n '$p' file # 打印最后一行
多命令执行
# 使用-e
sed -e 's/old1/new1/g' -e 's/old2/new2/g' file
# 使用分号
sed 's/old1/new1/g; s/old2/new2/g' file
# 使用大括号分组
sed '/pattern/{s/old/new/g; d}' file
awk文本处理
基本语法
awk [选项] '模式 {动作}' 文件
awk [选项] -f 脚本文件 文件
常用选项
选项 说明 -F指定字段分隔符 -v定义变量 -f从脚本文件读取
内置变量
变量 说明 $0当前整行 $1 $2 …第1、2…个字段 $NF最后一个字段 NF字段数量 NR当前记录号(行号) FNR当前文件的记录号 FILENAME当前文件名 FS字段分隔符(默认空格) OFS输出字段分隔符 RS记录分隔符(默认换行) ORS输出记录分隔符 RT记录终止符
基本用法
# 打印整行
awk '{print}' file
awk '{print $0}' file
# 打印特定字段
awk '{print $1}' file # 打印第1列
awk '{print $1, $3}' file # 打印第1和第3列
awk '{print $NF}' file # 打印最后一列
# 指定分隔符
awk -F: '{print $1}' /etc/passwd
awk -F, '{print $1, $2}' file.csv
# 打印行号
awk '{print NR, $0}' file
# 格式化输出
awk '{printf "%-10s %5d\n", $1, $2}' file
模式匹配
# 正则匹配
awk '/pattern/' file # 打印匹配行
awk '/pattern/ {print $1}' file # 打印匹配行的第1列
awk '$1 ~ /pattern/' file # 第1列匹配
awk '$1 !~ /pattern/' file # 第1列不匹配
# 条件匹配
awk '$3 > 100' file # 第3列大于100
awk '$1 == "root"' /etc/passwd # 第1列等于root
awk 'NR > 5' file # 第5行之后
# 组合条件
awk '$3 > 100 && $4 < 50' file
awk '$3 > 100 || $5 == "yes"' file
# 范围匹配
awk '/start/,/end/' file # start到end之间的行
awk 'NR>=10 && NR<=20' file # 第10-20行
BEGIN和END
# BEGIN - 处理前执行
awk 'BEGIN {print "Header"} {print} END {print "Footer"}' file
# 计算总和
awk '{sum += $1} END {print sum}' file
# 计算平均值
awk '{sum += $1; count++} END {print sum/count}' file
# 设置分隔符
awk 'BEGIN {FS=":"; OFS="-"} {print $1, $3}' /etc/passwd
# 打印行列数
awk 'END {print NR}' file # 总行数
awk '{print NF}' file # 每行字段数
数组
# 统计出现次数
awk '{count[$1]++} END {for (key in count) print key, count[key]}' file
# 分组求和
awk '{sum[$1] += $2} END {for (key in sum) print key, sum[key]}' file
# 数组赋值和访问
awk '{
arr[1] = "one"
arr[2] = "two"
print arr[1], arr[2]
}' file
# 删除数组元素
awk '{arr[$1]++} {delete arr["key"]} END {for (k in arr) print k}' file
流程控制
# if-else
awk '{if ($3 > 100) print $1, "large"; else print $1, "small"}' file
# for循环
awk '{for (i=1; i<=NF; i++) print $i}' file
# while循环
awk '{i=1; while (i<=NF) {print $i; i++}}' file
# for-in循环
awk '{count[$1]++} END {for (key in count) print key, count[key]}' file
内置函数
# 字符串函数
awk '{print length($0)}' file # 字符串长度
awk '{print substr($1, 1, 3)}' file # 子串
awk '{print toupper($1)}' file # 转大写
awk '{print tolower($1)}' file # 转小写
awk '{print index($0, "pattern")}' file # 查找位置
awk '{print split($0, arr, ",")}' file # 分割字符串
awk '{print gensub(/old/, "new", "g", $0)}' file # 替换
# 数学函数
awk '{print int($1)}' file # 取整
awk '{print sqrt($1)}' file # 平方根
awk '{print log($1)}' file # 自然对数
awk '{print exp($1)}' file # e的幂
awk '{print sin($1)}' file # 正弦
awk '{print cos($1)}' file # 余弦
# 其他函数
awk '{print srand()}' file # 设置随机种子
awk '{print rand()}' file # 随机数
awk '{print system("date")}' file # 执行系统命令
自定义函数
awk '
function max(a, b) {
return a > b ? a : b
}
{
print max($1, $2)
}' file
# 带局部变量的函数
awk '
function sum(arr, n, i, total) {
total = 0
for (i = 1; i <= n; i++)
total += arr[i]
return total
}
{
for (i = 1; i <= NF; i++) a[i] = $i
print sum(a, NF)
}' file
实用案例
日志分析
# 统计访问最多的IP
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
# 统计HTTP状态码
awk '{print $9}' access.log | sort | uniq -c | sort -rn
# 统计访问最多的URL
awk '{print $7}' access.log | sort | uniq -c | sort -rn | head -10
# 统计每小时的访问量
awk '{print substr($4, 14, 2)}' access.log | sort | uniq -c
# 计算平均响应时间
awk '{sum += $NF; count++} END {print "avg:", sum/count}' access.log
CSV处理
# 提取CSV列
awk -F, '{print $1, $3}' data.csv
# 过滤CSV行
awk -F, '$3 > 100 {print $0}' data.csv
# CSV转TSV
awk -F, 'BEGIN {OFS="\t"} {$1=$1; print}' data.csv
# 添加列标题
awk -F, 'BEGIN {print "Name,Age,Score"} {print}' data.csv
文本格式化
# 删除空行
sed '/^$/d' file
awk 'NF' file
# 删除行首行尾空格
sed 's/^[ \t]*//; s/[ \t]*$//' file
awk '{$1=$1; print}' file
# 每行添加行号
awk '{print NR, $0}' file
sed = file | sed 'N;s/\n/ /'
# 合并相邻行
sed 'N;s/\n/ /' file
awk '{printf "%s ", $0} NR%2==0 {print ""}' file
💡 文本处理建议 :
简单替换用sed,复杂处理用awk
大文件处理考虑使用mawk(更快)
复杂任务考虑使用Python/Perl
测试时先不加-i,确认结果后再修改文件
🔗 相关笔记 : 04.01_grep与正则表达式 02.06_Shell脚本 99.01_常用命令速查表