ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Linux正则表达式实战:grep、sed、awk与日志分析

Linux正则表达式实战:grep、sed、awk与日志分析 天天和 Linux 打交道的人迟早都要过正则表达式这一关。你迟早会遇到这样的情况日志文件几千行你想把某个 IP 段的所有访问记录拎出来配置文件里几十个参数你想精确找到某个 key 并改掉它的值或者你只是想统计一下系统里有多少种报错类型。这时候你会发现靠肉眼和鼠标是活不下去的正则表达式才是唯一靠谱的答案。我最早接触正则表达式是被 grep 里那串看起来像乱码的东西逼的。后来真正把它学透才发现这东西不是背几个符号那么简单——它背后是一整套匹配引擎的逻辑懂了这个逻辑你才能把 grep、sed、awk、vim甚至 Java、Python 里的正则全都串起来用。这篇文章我打算用从业者的视角把我这些年用 Linux 正则表达式的经验完整整理一遍。从正则的三大流派讲起到元字符的逐个拆解再到 grep 和 sed、awk 里的实战用法最后把我踩过的坑和排查套路全部交代出来。不管你是在准备 Linux 运维面试还是做日志分析和数据处理照着这篇文章练一遍基本就够用了。1. 正则表达式在 Linux 里的定位与价值1.1 它到底是什么能解决什么问题正则表达式Regular Expression简称 regex本质上是一套描述文本模式的迷你语言。你写一个模式让工具去文本里找长得像这个模式的内容。和通配符不一样通配符只能做简单的文件名匹配*.log而正则可以做精确到这个位置是数字、那个位置必须出现三次、再往后可以有大写字母也可以没有这种复杂描述。在 Linux 里正则表达式是文本处理三兄弟——grep、sed、awk——的共同基石。运维场景里最常见的几个需求全都要靠它从几百 MB 的日志文件里筛选特定时间段的请求记录比如只看 10:00 到 10:15 之间的 5xx 报错提取一行文本中的某一部分比如从192.168.1.100 - - [10/Oct/2024:13:55:36 0800] GET /api/user?id123 200里把状态码、URI、IP 分别抠出来批量修改配置文件里某个参数的值比如把所有的worker_processes 1换成worker_processes 4校验用户输入比如判断一个字符串是不是合法的 IPv4 地址、身份证号或者邮箱说白了正则表达式解决的问题就一句话用规则代替人工在无规律的文本里找有规律的子串。它不能凭空创造信息但能把隐藏规律的东西快速提取出来。1.2 三种流派BRE、ERE、PCRE 怎么选很多新手最大的困惑是——为什么同一个正则在 grep 里这样写能用在 vim 里就不能到了 Python 里又变了这是因为正则存在三种主流引擎风格Linux 工具各自继承了不同的流派。BRE基本正则POSIX 标准定义的基本正则。特点是?和这些字符默认是普通字符必须加反斜杠转义成\?和\才有特殊含义。默认模式下的 grep、sed 都用它。ERE扩展正则在 BRE 基础上把?、、|、()变成原生支持不用转义。grep -E、sed -r、awk都用它。PCREPerl 兼容正则功能最强支持非贪婪匹配、前瞻断言、反向引用这些高级特性。grep -P如果你的 grep 编译了 PCRE 支持、vim的某些模式、Python 的re模块、Java 的Pattern都用它。我个人的建议是在纯 Linux 命令行环境下优先用 ERE也就是记住grep -E和sed -E新版本 sed 推荐用-E-r是老写法。理由很简单——ERE 让、?、(、)、|直接可用心智负担小而且不依赖 grep 是否编译了 PCRE 扩展兼容性最好。说到 PCRE 那套高级特性其实日常工作里 90% 的场景用不上。如果你发现自己写正则写得很花哨比如用了大量前瞻断言先停下来想想是不是模式本身设计得太复杂了。简洁才不容易出 bug。2. 核心语法拆解从元字符到组合拳不管用哪种流派正则的基本元素就那几类。我把它们拆开讲透每个都配上 Linux 里的实际例子。2.1 字符匹配与字符类最基本的地基正则最底层的单位是匹配一个字符。普通字符比如a、1、自己在正则里是什么就是什么直接匹配自身。但有一批特殊字符拥有魔法叫元字符。第一类就是字符类.匹配任意单个字符换行符除外[abc]匹配括号中的任意一个字符[a-z]匹配范围内的字符[^abc]匹配不在括号内的任意字符\d匹配数字等价于[0-9]PCRE 支持\w匹配单词字符等价于[A-Za-z0-9_]\s匹配空白字符包括空格、Tab、换行等这里有个我早年常犯的错——.不是任意内容的万金油。它不能匹配换行符所以在处理多行文本时grep默认一行一行地处理文本.自然不跨行。你想匹配任意一个非换行字符时它很好用但想在多行之间跳过任意内容就得想别的办法。字符类里还有一个隐藏细节短横线-在字符类中的位置决定它是普通字符还是范围符号。[a-z]里的-是范围连接符但[-az]或[az-]里的-就是普通字符。这个坑在写 IP 段、日期范围匹配时特别容易踩。2.2 数量限定与贪婪模式控制出现多少次光能匹配单个字符远远不够还得规定它出现多少次。这是第二类元字符*前面的字符出现 0 次或多次前面的字符出现 1 次或多次ERE/PCRE?前面的字符出现 0 次或 1 次ERE/PCRE{n}前面的字符恰好出现 n 次{n,}前面的字符至少出现 n 次{n,m}前面的字符出现 n 到 m 次默认情况下这些量词都是贪婪的——匹配引擎会尽可能多地吞字符。举个例子正则a.*b去匹配a123b456b贪婪模式下匹配结果是整个a123b456b而不是a123b。因为引擎会先把.*撑到最大再回溯找最后一个b。这个特性在提取数据时很容易伤人。比如你想从title我的博客/title正文...里提取标题内容用grep -o title.*/title会得到从第一个title到最后一个/title之间的所有东西而不是靠近的标题文本。解决办法大体有两种一是用非贪婪模式.*?PCRE 支持但在 grep 默认工具里不一定方便二是把正则写得更精确比如用[^]*代替.*因为中间内容按理说不含这样写既躲避了贪婪问题也不需要 PCRE 加持。我给你的忠告是——慎用.*多用字符类约束范围。能用[^字符]的不要用.。这是从一次次误匹配里练出来的肌肉记忆。2.3 锚点、分组与引用定位与复用有了字符和数量你还得告诉引擎从哪开始、到哪结束、哪些部分要记下来。^锚定行首$锚定行尾\b匹配单词边界PCRE/部分工具支持()分组把一段模式当作整体也能捕获匹配内容|多选分支表示或\1、\2反向引用引用前面第 1、2 个分组捕获的内容锚点的价值被严重低估。新手写grep error觉得没问题但要精确匹配以 error 开头的行用^error只匹配以 error 结尾的行用error$。这两个符号能把匹配范围从包含提升到边界精确避免误伤一大堆无关行。分组的价值在于两点一是把一组字符当成整体施加量词比如(ab)匹配ab、abab、ababab二是捕获内容供后续使用比如 sed 替换时用\1拿回第 1 组的内容。这个特性在数据清洗里是核武器。反向引用最经典的例子是匹配成对标签。你想找abc123abc这种前后内容相同的字串正则就是(abc)\d\1。再比如校验 HTML 标签成对([A-Za-z]).*?/\1——注意这里带上了非贪婪.*?因为标签内容不该跨越多个标签。2.4 多选分支与转义陷阱最容易出错的两个地方|是最直观也最容易出错的元字符。它的优先级最低也就是说^abc|def$实际含义是以 abc 开头或者以 def 结尾而不是以 abc 或 def 开头且结尾。想让abc 或 def成为一个整体必须加括号^(abc|def)$。再来说转义。正则里的特殊字符如果要匹配它的字面意义需要加反斜杠。.要写成\.*要写成\*(要写成\(。但注意流派区别——BRE 里?默认是字面?要表示量词反而得写\?ERE 里?默认是量词要匹配字面?又得写\?。同一个写法在两个流派里含义完全相反这是跨工具复用时最隐蔽的坑。我举个例子。假设你要从文本里找出所有包含文件.txt字样的行grep -E 文件\.txt是正确写法因为 ERE 里.是元字符要匹配字面点必须转义。如果你用grep 文件.txt默认 BRE.仍然是元字符所以照样能匹配文件atxt这样的怪行。所以不管哪个流派字面点一律写\.就对了。3. 实战三大文本工具的 regex 用法语法是武器工具是使用方法。同样是正则放到 grep、sed、awk 里各有各的打法。这一章我用同一份示例文本把三个工具串起来讲。先创建一个演示文件access.log192.168.1.101 - - [10/Oct/2024:13:55:36 0800] GET /api/user?id123 HTTP/1.1 200 1024 192.168.1.102 - - [10/Oct/2024:13:56:02 0800] POST /api/login HTTP/1.1 500 512 172.16.3.88 - - [10/Oct/2024:13:57:19 0800] GET /static/app.js HTTP/1.1 304 0 10.0.0.55 - - [10/Oct/2024:14:01:47 0800] GET /api/user?id456 HTTP/1.1 200 2048 192.168.1.101 - - [10/Oct/2024:14:05:33 0800] DELETE /api/user?id123 HTTP/1.1 403 2563.1 grep最快上手的过滤器grep 的正则职责只有一个——筛选行。它不管你匹配到的内容在哪、是什么只看这一行整体是否符合模式匹配就输出整行。最常用的几个参数-E使用 ERE 语法-o只输出匹配到的部分而不是整行-v反向匹配输出不符合的行-i忽略大小写-w精确匹配整个单词-c统计匹配行数-n显示行号场景一统计 13:55 到 13:59 之间的请求。日志时间都在[10/Oct/2024:HH:MM:SS字段里可以直接匹配grep -E 10/Oct/2024:13:(5[5-9]|6[0-9]) access.log这段正则里(5[5-9]|6[0-9])就是分组的正确用法——匹配分钟数 55 到 69虽然 60 到 69 在实际中不存在但在正则层面表述了这个区间范围内的字符组合用|把两种范围情况并起来。场景二统计 5xx 错误有多少条grep -E [5][0-9]{2} access.log注意我先写了一个空格再加再匹配状态码三个数字最后又一个空格这是为了利用日志里双引号和空格作为锚定边界避免把 URI 里的数字误当成状态码。场景三提取所有访问过/api/user的 IP不去重地列出来。这一步要用-o配合分组禁止捕获错了位置grep -E ^([0-9]{1,3}\.){3}[0-9]{1,3} access.log这里的([0-9]{1,3}\.){3}是什么意思[0-9]{1,3}匹配 1 到 3 位数字后面跟一个.整个分组重复 3 次然后最后再匹配 1 到 3 位数字。这么写会比^.*?精确得多不会把后面的 IP如果有多个 IP 字段的话也吞进来。实际上这个 IP 模式还比较宽松它允许999.999.999.999这种非法值。严谨的 IPv4 校验得这么写^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])$这个模式的计算逻辑是这样的IPv4 的每一段范围是 0 到 255拆成三种情况——250 到 255 写25[0-5]200 到 249 写2[0-4][0-9]100 到 199 写1[0-9]{2}0 到 99 写[1-9]?[0-9]。四个段用.连接前三段后带点、最后一段不带。这个正则看起来长但每一段都有明确意义安全校验场景里不能为了简洁丢掉严谨性——这就是我说的为什么。3.2 sed流编辑里的 regex 之王sed 的核心能力是查找替换。基本命令格式sed s/正则/替换内容/标志 文件其中s是 substitute 的缩写/是分隔符可以用#或|代替如果正则本身包含斜杠标志常用g全局替换默认只替换每行第一个匹配、i忽略大小写。场景一把日志里的 IP 前两位192.168替换为10.10sed -E s/192\.168\./10.10./ access.log-E让我不需要把()转义。为什么要对.转义因为我想匹配的是字面点.而不是任意字符。如果不转义写成192.168.它还能匹配192x168y这种诡异的组合。场景二从访问日志里提取状态码和 URI输出成 CSV。这要用到分组捕获sed -E s/^.*([A-Z]) ([^]*) HTTP\/1\.1 ([0-9]{3}) .*$/\1,\2,\3/ access.log拆解一下^.*跳过行首到双引号之前的内容([A-Z])捕获请求方法如GET、POST([^]*)捕获请求路径——[^]*表示任意数量、但不包含双引号的字符这是为了避免路径里如果有空格或符号不至于把后面的也吞进来HTTP\/1\.1匹配常规模板([0-9]{3})捕获三位状态码最后.*$吃掉剩余部分。替换部分用\1、\2、\3把捕获组重新拼装。这就是分组和反向引用的实战价值——你的正则不只是匹配还能重组数据。这条命令跑完日志就变成了三列方法、路径、状态码可以直接喂给 awk 或者导入表格做统计。场景三只保留匹配内容删除其他。还是用上面这个例子改成sed -E s/^.*([A-Z]) ([^]*) HTTP\/1\.1 ([0-9]{3}) .*$/\1 \2 \3/ access.log本质上和 CSV 输出是一样的思路替换成空格分隔的格式。sed 里做这种提取字段比 awk 慢但如果只是临时看一眼用它完全够。3.3 awk字段处理中的 regexawk 和 grep、sed 的最大区别在于它把每一行按分隔符拆成字段默认空格/Tab然后你可以对每个字段做正则匹配。这让 awk 在做统计时是无敌的。awk 里的正则匹配主要有两种写法。一种是直接用/模式/作为条件筛选行后处理另一种是用~运算符匹配某个具体字段。场景一统计每个 IP 发了多少请求awk {print $1} access.log | sort | uniq -c | sort -rn这条虽然没直接写正则但展示了 awk 字段思想的威力——$1就是第一列也就是 IP。配合sort和uniq -c最原始的去重统计就有了。场景二统计 500 错误的来源 IPawk $NF 500 {print $1} access.log | sort | uniq -c$NF表示最后一个字段。awk 会把整行按空白分割倒数第二列其实是返回字节数最后两列才是状态码和字节数。这里用$NF 500精确匹配最后一列的数值。注意是做精确比较如果用~ /500/就会误匹配到 URI 里包含 500 的请求。场景三筛选时间在 13:55 之后的 2xx 请求并输出 IP 和状态码awk /13:(5[5-9]|6[0-9])/ $8 ~ /^2[0-9]{2}$/ {print $1, $8} access.log这里我拆成两个条件/13:(5[5-9]|6[0-9])/匹配时间字段注意 awk 默认会在整行上找这个模式$8 ~ /^2[0-9]{2}$/用~对第八个字段做正则^和$确保它是一整个 2xx 状态码而不是包含 2 和两个数字的字符串。awk 还有一个杀手级用法用match()函数提取正则匹配到的子串的位置和长度再用substr()抠出来。这个组合在做复杂字段提取时比纯 sed 更清晰。不过日常使用频率不高知道有这条路就行真遇到棘手文本时再翻 man 手册。3.4 其他工具vim 和 find 里也要会正则不止属于 grep 家族。vim 的搜索模式里可以用正则比如/error\c忽略大小写搜索 error。vim 的替换命令:%s/foo/bar/g本质和 sed 一模一样只是作用范围是整个文件。find命令默认不支持正则但支持通配符不过它有一个-regex参数可以按正则匹配整个路径。比如查找当前目录下所有.conf后缀但排除backup目录下文件的写法是find /etc -type f -regex .*\.conf$ ! -path *backup*这里.*\.conf$是在整条路径上做匹配所以必须用.*开头。还有一个容易忽略的场景是less和more里按/搜索时也支持正则less里甚至能直接输入^192跳到以 192 开头的行。这些技巧在排障时非常好用问题是很多人不知道它们和 grep 用的是同一套规则。4. 常用场景模式库与参数计算光会语法还不够你得有看到问题就知道写什么模式的条件反射。下面这几个模式是我在实际运维中反复用的可以直接抄。4.1 日志分析时间、IP、状态码三板斧时间匹配很容易在月份和日上翻车。10 月 13 日的通用模式要写成10/Oct/2024这种固定文本因为月份名是字母没有捷径。但如果你的日志是数字月份比如2024-10-13就可以用2024-(10)-(1[0-9]|2[0-9]|3[01])来匹配整个 10 月的日期其中1[0-9]覆盖 10 到 19 号2[0-9]覆盖 20 到 29 号3[01]覆盖 30 和 31 号。IP 匹配的高性价比写法是([0-9]{1,3}\.){3}[0-9]{1,3}用于日志场景够用。如果是要校验输入数据严谨性才需要我之前写的那一长串 IPv4 全量模式。状态码的匹配要区分精确和模糊。查所有 4xx 和 5xx用[45][0-9]{2}只查 500用500。但手工写引号容易出错我更推荐直接交给 awk 的字段比较。4.2 数据清洗提取、重组、校验从文本里提取邮箱这个模式可以满足绝大多数场景[A-Za-z0-9._%-][A-Za-z0-9.-]\.[A-Za-z]{2,}拆开看前半段是邮箱用户名部分允许数字、字母、点、下划线、百分号等是分隔域名部分允许字母数字和连字符可以有多级子域最后\.[A-Za-z]{2,}要求顶级域名至少两个字母。提取 URL https?://[A-Za-z0-9.-](:[0-9])?(/[^ ]*)?这里(:[0-9])?用?让端口部分可有无(/[^ ]*)?让路径部分可有无。注意路径部分用的是[^ ]*如果日志里的 URL 后用空格隔开其他字段这样写就不会把多余内容吞进来。校验身份证号18 位这是我在 Java 面试题场景里反复见过的题目^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$这里最考验智商的是日期部分月份是(0[1-9]|1[0-2])也就是 01 到 09 或 10 到 12日期是([0-2][1-9]|10|20|30|31)覆盖 01 到 29 再加 10、20、30、31。严谨的校验还要处理 2 月 30 日这种情况那得写更复杂的组合甚至用程序判断正则一个人搞不定全活。所以我的原则是——正则做格式粗校验业务逻辑做精校验。4.3 运维批处理改配置、清数据批量改配置文件的套路我再说一个典型例子。Nginx 配置里有若干行注释想把所有被注释掉的listen 80的 server 块解除注释sed -E s/^#(\s*listen 80;)/\1/ nginx.conf这里面^#匹配行首的注释符#\s*允许#和listen之间有任意空白整个分组(\s*listen 80;)捕获了原本的配置内容。替换时用\1把捕获内容写回去等于把#删掉了。这个删掉行首标记但不删内容的思路比sed s/^#//更安全因为它精确锁定了listen 80这一个目标不会误删其他行首的#。再比如清理日志里的 ANSI 转义序列就是终端里的那些\033[31m彩色控制符用sed -E s/\x1b\[[0-9;]*[a-zA-Z]//g colored.log\x1b是 ESC 字符的十六进制表示\[匹配左方括号字面量[0-9;]*匹配颜色参数数字和分号[a-zA-Z]匹配结尾的字母命令符。这个模式在抓取程序输出做处理时非常实用。4.4 构建自己的常用正则速查表我建议你维护一个自己的速查文档把平时验证过的模式存下来。这不是偷懒而是工程化的好习惯。表格的形式比记在脑子里强得多匹配目标正则模式说明IPv4 地址宽松([0-9]{1,3}\.){3}[0-9]{1,3}日志场景够用IPv4 地址严格((25[0-5]2[0-4][0-9]邮箱[A-Za-z0-9._%-][A-Za-z0-9.-]\.[A-Za-z]{2,}基础校验时间YYYY-MM-DD20[0-9]{2}-(0[1-9]1[0-2])-([0-2][1-9]从日志提取 URI([A-Z]) ([^]*) HTTP配合分组捕获我自己用这套速查表解决过至少几百次现查现写的窘境。反正经验就是——正则这种东西你不用就会忘用了就发现处处是它。5. 常见问题与排查技巧实录5.1 我的正则为什么匹配不到先检查这五处正则不匹配的原因往往不在正则本身而在周围环境的假设错了。我按排查优先级列一下第一流派错了。你写\d想匹配数字结果用的是grep默认 BRE 模式BRE 不认识\d。解决方法是统一加-E或用[0-9]。我见过太多人在这上面耗半小时。第二转义多了或少了。在 ERE 里(直接是分组写成\(反而变成字面括号。你在 ERE 工具里写\(abc\)匹配的是文本里真有括号的(abc)而不是捕获组。这属于越想匹配越匹配不到的经典陷阱。第三贪婪导致匹配范围过大。.*一口气吞到行尾后面如果还跟其他条件引擎会回溯去找满足条件的位置。这时候如果你期望的匹配结果是最短的多半就错了。看一眼匹配结果是不是包含太多内容优先换成[^...]写法。第四锚点假设错了。你以为^匹配字段开头但 grep 里^匹配的是行首。字段开头的匹配得用\b或awk的$1 ~ /^模式/。这种概念混淆在从文本处理转到 awk 时最容易出现。第五文件里有隐藏字符。Windows 传过来的文本每行结尾有\r你写$匹配行尾但实际匹配到\r前面就停住了。用cat -A 文件看看行尾是不是^M$如果是用sed -E s/\r$//先清掉。5.2 匹配太慢性能问题的三大元凶正则表达式写不好会在大数据量文本上卡到怀疑人生。最常见的原因有三个。第一个是灾难性回溯。当正则里有多个量词连在一起比如(a)b去匹配一个很长的、由a组成但没有b的字符串时引擎会尝试无数种切分方式时间呈指数爆炸。这类正则被称为ReDoS漏洞严重的甚至能把服务器拖垮。解决办法是简化嵌套量词尽量用[^...]代替.*避免(x)这种结构。第二个是扫了整个文件只为了找几个模式。如果你要对一个 1GB 的日志跑 20 个不同的 grep不要傻乎乎地跑 20 遍。用一次grep -E把多个模式用|合并或者用 awk 一个进程内处理多条件。IO 成本才是实际瓶颈。第三个是没利用好锚点和固定文本。^192会比.*192.*快得多因为引擎可以立即从行首开始而不是扫描全行找 192 的位置。写正则时尽量把最确定的字面量放在前面。记得有一次在线上环境我用grep -E (error|fail)..* huge.log查问题跑了半天没出结果后来发现(error|fail)前面没有任何锚点后面还挂着.*导致每一行都要回溯。改成^(error|fail)后几乎瞬间出结果。这让我彻底记住了锚点精简量词这两条提速铁律。5.3 快速验证与调试的正规方式调试正则不要在生产环境上一个一个试。我推荐两个路子。第一个是命令行快速验证。用echo造一行测试数据直接喂给 grepecho 192.168.1.101 - GET /api/user HTTP/1.1 200 | grep -E -o ([A-Z]) [^]*加了-o后你能立刻看到到底匹配出了什么。如果输出为空或者匹配到意想不到的内容调整正则的速度会快很多。第二个是用 Python 交互式调试如果机器上有的话python3 -c import re; print(re.findall(r(\d{1,3}\.){3}\d{1,3}, 192.168.1.101))Python 的re模块支持 PCRE 的大部分语法findall的结果会直接告诉你匹配到哪些内容还能看到分组捕获的细节比一次次敲 grep 清晰得多。最重要的一条调试经验是先让模式能匹配到一个最小示例再把示例逐步复杂化。不要一上来就写一个 100 个字符的 IP 校验大正则然后对着真实数据发懵。先匹配192.168测试通再加\d{1,3}再更新为严格校验每一步都验证最终组合起来才不会出错。5.4 常见错误速查表错误写法错误原因正确写法grep \d fileBRE 不认识\d和grep -E [0-9] filegrep -E (abc file括号不配对语法错误grep -E \(?abc\)? file或检查是否漏写sed s/a\.b/1/g想匹配a.b却写成s/a.b/1/g点号未转义匹配到任意字符sed s/a\.b/1/gawk $2 ~ /[0-9]/想匹配第二个字段是数字未加^$匹配到包含数字的字段awk $2 ~ /^[0-9]$/grep -E ^abcd$ file|优先级低含义是以 ab 开头或以 cd 结尾这张表是我把团队新人问得最多的几个问题整理出来的前两条几乎每周都会出现。6. 经验沉淀与效率技巧6.1 正则写到什么程度算够用很多朋友学正则容易走两个极端。一个极端是把所有符号背完觉得自己什么都能写结果一实战就翻车另一个极端是只会用最简单的grep 关键字遇到复杂场景只能靠脚本暴力处理。我的经验是在 Linux 里你真正需要牢记的元字符不超过 15 个.、[]、^、$、*、、?、{}、()、|、\再加上字符类简写\d、\w、\s和反向引用\1。把这 15 个符号用熟配合 grep -E、sed -E、awk 的字段机制就能解决 95% 的日常文本问题。PCRE 那些高级特性等真的遇到时再查即可没必要提前背。6.2 记忆口诀与思维模型我建议用一个模型理解正则把正则当成一张筛选模板引擎拿着这张模板去扫描文本每到一个位置尝试匹配匹配失败就移动到下一个位置继续试。想清楚匹配是扫描尝试的过程就能理解为什么贪婪会回溯、为什么锚点能提速、为什么分组能捕获。对于元字符记忆我自己的联想方法是.是任意一个像地雷一样到处都可能踩到*是有也行、没有也行、多也行是至少来一个?是可选像要不要加个辣椒{n}是精确计数。这种土味记忆法虽然不正经但确实帮我快速建立了条件反射。6.3 三步法从需求到正则的推导路径最后我把写正则的完整流程总结成三步你按这个顺序走基本不会乱。第一步划出目标边界。明确你要匹配的是整行还是行内片段如果是行内片段它前后的固定文本是什么比如日志里五开头的状态码你得先确认它前后是空格和引号这就是边界。第二步拆解结构成小块。把目标拆成前缀 主体 后缀三块比如 IP 就是1-3位数字点重复三次再加一个1-3位数字。每一块写一个最小的子正则分别验证。第三步组合并用数据验证。把小块拼成大正则先在样例上验证再把样例扩展到多种真实情况。最后如果还担心误匹配加-o查看匹配结果、或先用 awk 打印字段确认位置再上生产。这三步写出来的正则出问题的概率远低于凭空想的。我自己帮同事排查正则问题时几乎都是按照这个顺序帮他把需求重新梳理一遍结果大多数时候不是语法错误而是需求边界没搞清楚。正则这块说到底是熟练工的活儿。你用过一百次之后看一段日志就知道该怎么拆踩过几次坑之后一眼就能看出别人的正则哪里有隐患。希望这篇文章能帮你把第一块垫脚石踩实了剩下的到真刀真枪的日志和配置文件里去练吧。
返回列表