
凌晨三点手机震动。我摸黑爬起来屏幕上显示“生产环境访问异常”的告警。那种心跳加速的感觉做过运维的都懂。但这次我已经从运维岗转到了安全方向手里的工具没变变的只是看问题的角度。日志就在那几百万行躺着静悄悄地记录了一切。关键是你知不知道看哪里、怎么看。很多人一听“安全分析”就觉得是高大上的 SIEM 平台、复杂的攻击链还原其实真到了应急响应那一刻先用顺手的反而是最朴素的几个 Linux 命令。我转安全这几年最深的体会是那些年在运维岗上天天用的基本功一样都没有浪费。这篇文章我就聊聊自己从运维转安全后用的最多的 5 个日志分析命令 — grep、awk、sed、sort、uniq以及它们到底怎么在安全排查里组合起来干活。适合正在考虑转安全的运维朋友、刚入门的安全新人也适合那些觉得日志分析很玄乎、想从实操入手的同学。看完你会发现转型的第一步不是学新东西而是把已经会的东西换个思路用。1. 为什么日志分析是运维转安全的第一站1.1 日志在运维眼里是告警在安全眼里是案发现场做运维的时候我看日志是为了回答三个问题系统为什么慢了服务为什么挂了磁盘为什么满了那时候日志是排障工具只要把错误信息捞出来、定位到具体模块任务就算完成。转了安全之后同样一份日志要回答的问题完全变了——谁进来了怎么进来的进来之后干了什么已经走了还是还在同样是看/var/log/secure运维关心的是“哪个用户登录失败了”安全关心的是“同一个 IP 在五分钟内试了一百次密码是不是在爆破”。这种视角的转换不需要把 Linux 推倒重学。恰恰相反用的还是那套管道符、重定向、正则表达式。日志分析本质上是文本处理安全日志和业务日志在格式上没有本质区别区别只在于你要从里面提取什么信号。1.2 为什么先学组合命令而不是直接上安全分析平台有些朋友上来就研究各种复杂的日志分析平台、可视化大屏我觉得顺序反了。平台是辅助底层能力才是核心。原因有几个第一应急响应往往发生在客户现场或者老旧的跳板机上环境简陋到什么程度都不奇怪可能连包管理器都不让你装。这时候你总不能说“等我搭个平台再排查吧”。手里有 Bash就什么都不慌。第二安全分析平台展示的告警、趋势图底层逻辑依然是筛选、聚合、排序、统计。搞懂 grep 怎么做条件过滤、awk 怎么做字段提取、sort 和 uniq 怎么做排序去重再用任何平台都能一眼看穿它背后的查询逻辑不会被参数和图表带偏。第三日志分析不是一次性的动作而是一个反复迭代的过程。你可能要先快速筛一遍看有没有明显特征再深入提取数据再换个维度重新看。这种快速试错的节奏靠命令行管道是最舒服的改一个参数、加一个条件立刻就能跑出新结果。1.3 五个命令的分工与协作关系我给自己总结过一个非常朴素的日志分析流水线先缩小范围再拆解字段然后排序、去重、计数最后得出结论。五个命令正好对应这条流水线的五个环节。grep 负责条件过滤像一道筛子把几百万行日志里符合条件的行捞出来。awk 负责字段拆解和统计把日志行按空格或逗号切开提取 IP、状态码、耗时这些关键信息还能直接做累加计数。sed 负责区间截取和文本变形在大量日志里切出特定时间窗口的内容或者做敏感信息脱敏。sort 负责排序让数据从杂乱无章变成有规律可循。uniq 负责去重和计数配合 sort 使用能快速找出“哪个 IP 最活跃”、“哪种攻击特征出现最多”。单个命令都很简单难的是把它们串起来。管道符加起来的战斗力远超任何一个命令单独使用的效果。下面我按自己在真实排查里用得最多的场景把这五个命令逐一拆开讲清楚。2. grep安全日志的第一道筛子2.1 从一次网站被刷说起去年帮某客户排查一次网站访问异常。客户的反馈是首页响应很慢打开要十几秒。按运维思维先查负载、查连接数指标看着都还算正常但业务就是卡。后来我去翻了 nginx 的 access.log先用 grep 做了个很简单的过滤grep -c HTTP/1.1\ 502 /var/log/nginx/access.log执行完我就有点数了过去一小时里 502 出现了两千多次正常情况下这个数字应该是几十。网站的故障表象是慢但真正的异常逻辑藏在日志的这列状态码里。这就是我要说的第一件事grep 是最基础的筛选工具但它在安全分析里的用法和运维排障时不完全一样。运维通常用 grep 找某个明确的错误关键字比如grep OutOfMemory。而安全分析时你要筛的往往不是“确定发生了什么”而是“有没有我没见过的异常迹象”所以反过来我更多用 grep 去“排除”噪音把正常的请求过滤掉剩下的就是可疑的东西。2.2 安全场景里真正有用的几个 grep 用法基础语法我就不啰嗦了grep [选项] 模式 文件。我重点说几个实战里高频用到的选项。第一个是-E扩展正则。在 access.log 里分析可疑扫描行为时经常要同时匹配多个特征。比如想筛出所有访问路径里带admin、manager、phpmyadmin的请求一句话就搞定grep -E /admin|/manager|/phpmyadmin /var/log/nginx/access.log | head -100第二个是-v反向匹配。健康检查、监控探针这类请求会污染分析结果每次都得先踢掉它们grep -v -E User-Agent: (ELB-HealthChecker|kube-probe) /var/log/nginx/access.log第三个是-c统计数量。不要一上来就全量看内容先计数数量异常本身就是信号。比如想看某台机器上 sshd 登录失败的次数直接grep -c Failed password /var/log/secure2.3 一个必须养成的习惯IP 匹配一定要加边界这个坑我踩过好几次。比如你想筛来自某个 IP 的请求直接写grep 192.168.1.100可能会把192.168.1.1000、192.168.1.1001这种根本不存在的地址也带进来更麻烦的是会把192.168.1.100.5这类畸形请求混进来。因为 grep 是子串匹配它不管字符前后是什么。正确姿势是给 IP 加上边界保证匹配到的是完整的地址grep -E (^|[^0-9.])192\.168\.1\.100([^0-9.]|$) /var/log/nginx/access.log看着麻烦但这条命令的价值在于当你把可疑 IP 的特征写到告警规则、写到自动化脚本里时不会因为草率匹配产生大量误报。运维阶段的教训是“日志里有噪音分析不出结果”安全阶段的教训是“误报太多真正告警就没人在意了”。2.4 我的实操建议把 grep 特征写成清单用 grep 做安全分析最大的忌讳是每次都在命令行里临时编正则想到一个写一个结果反复敲一长串。我会把常用的筛选用法沉淀成一个小脚本或者直接写成 grep 特征清单文件一行一个模式配合-f参数使用grep -f suspicious_patterns.txt /var/log/nginx/access.log这么做的好处是积累的特征可以复用、可以沉淀。比如你发现某段时间有人在批量扫描某个漏洞路径把路径特征加进清单里下次遇到类似情况直接跑一遍就能筛出来。运维的巡检脚本思路用到安全日志分析上完全行得通。3. awk把日志行拆开数据才真正开始说话3.1 日志是一行一行的文本但信息藏在字段里grep 帮你把可疑的行筛出来了但行只是原材料。真正有价值的分析发生在把一行日志拆成一个个字段之后。我当年在运维岗上对 awk 的印象就是“处理列的工具”打印第一列、第三列数据。转安全后我发现它的价值远不止于此。举个最典型的场景分析 sshd 爆破攻击。日志里相关的那一行长这样Mar 15 03:12:44 webserver sshd[25631]: Failed password for root from 203.0.113.45 port 54321 ssh2我想知道的是有哪些 IP 在尝试爆破每 IP 试了多少次用 grep 能做但很费劲用 awk 就是几秒钟的事awk /Failed password/ {print $(NF-3)} /var/log/secure | sort | uniq -c | sort -rn解释一下这条命令干了什么/Failed password/是模式匹配只处理包含这个关键字的行$(NF-3)表示当前行的倒数第 4 个字段。上面那行日志里倒数第 4 个字段就是 IP 地址203.0.113.45。这就体现了 awk 的一个思维转变它不关心字段的绝对位置而可以用NF字段总数来从后往前取。因为 sshd 日志的 IP 位置相对固定但前面日期、主机名的空格数可能不固定从后面往前数反而更稳定。运行结果会是一串 IP 和计数一眼就能看出哪几个 IP 在疯狂尝试哪些只是零星几下。这个输出就是后续封禁和溯源的基础。3.2 用 awk 做字段提取的细节全看分隔符awk 默认按空白字符拆分字段空格和 Tab 都算分隔符。但很多日志不是用空格分隔的比如 access.log 的字段之间是空格没错但请求行本身包含空格再比如某些业务日志用逗号、竖线分隔。这时候要显式指定分隔符。看一个实际例子。nginx access.log 的默认格式大概是203.0.113.45 - - [15/Mar/2025:03:12:44 0800] POST /login HTTP/1.1 200 532 - Mozilla/5.0想提取“客户端 IP、请求方法、URL、状态码”可以这样写awk {print $1, $6, $7, $9} /var/log/nginx/access.log这里$6是请求行里的方法POST$7是路径/login$9是状态码。注意$6前面带一个双引号POST所以如果想拿到干净的字段要稍微处理一下。这个细节在写分析命令时要心里有数。如果日志是 JSON 格式的或者字段用逗号分隔就指定-Fawk -F, {print $2, $5} /var/log/app.log3.3 用 awk 直接做统计和累加awk 不仅能提取字段还能在提取的同时做运算。这是它比纯文本工具更强的地方。假设我怀疑某个接口在被恶意刷想统计每个来源 IP 对/api/order的请求次数。一条命令就能得到结果awk $7 /api/order {count[$1]} END {for (ip in count) print ip, count[ip]} /var/log/nginx/access.logcount[$1]的意思是拿第一列IP当数组下标每出现一次就加 1。END块里遍历数组把每个 IP 和它的计数打印出来。如果再配合sort -rn排名就出来了。这种思路在安全分析里非常常用。比如统计每个来源 IP 触发了多少 WAF 拦截规则、统计每个用户的登录失败次数、统计某个时间窗口内异常状态码的分布。只要日志里能拆出维度字段awk 就能按这个维度做聚合。3.4 给刚上手的人提个醒awk 脚本别写复杂awk 能做的事情非常多条件、循环、函数、格式化输出都能来。但我的建议是日常安全分析里awk 就干两件事——拆字段、做累加。超过这两件事要么拆成多步命令要么写独立的脚本。原因很简单命令行里写复杂 awk 脚本可读性太差。你自己写的命令过两天回来看可能都想不起来逻辑是什么更别说团队协作的时候别人怎么看得懂。我见过有人在命令行里写一长串 awk 处理逻辑结果一个括号没配对调试了半天。真正高效的排查流程是先用简单命令快速确认方向再慢慢完善细节。复杂逻辑放进脚本文件加上注释才是正路。比如上面的统计需求我会把 IP 列表先抽出来awk $7 /api/order {print $1} /var/log/nginx/access.log ip_list.txt然后交给下面要讲的 sort 和 uniq 去处理。每个阶段只做一件事清晰又不容易出错。4. sed按时间和位置精准切日志4.1 分析大日志前先切出时间窗口日志文件动辄几个 GB直接在全文上跑分析不是不行但效率很低。安全排查几乎总是围绕一个明确的时间窗口展开的告警是 14:30 触发的那就要看 14:20 到 14:40 之间发生了什么。这时候 sed 就派上用场了。sed 是流编辑器它的经典用法是按地址范围处理文本。按时间窗口切日志用的是地址范围配合打印操作sed -n /25\/Mar\/2025:14:20/,/25\/Mar\/2025:14:40/p /var/log/nginx/access.log window.log这条命令的意思是从匹配25/Mar/2025:14:20的行开始到匹配25/Mar/2025:14:40的行结束把这个范围内的所有行打印出来重定向到一个新文件。-n是关闭默认输出只打印我们指定的内容。切出来之后后续的 grep、awk 操作都在这个临时文件上跑速度会快很多也不会被窗口之外的历史数据干扰。4.2 sed 匹配时间窗口时最容易踩的坑我一开始用这个方法时遇到过一种很隐蔽的问题如果我指定的起止时间字符串不够精确sed 可能会从错误的地方开始或者结束。举个例子access.log 的行里时间格式是[15/Mar/2025:03:12:44 0800]。如果我写sed -n /Mar 15 03/,/Mar 15 04/p access.log注意这个模式可能会匹配到Mar 15 03但它也会匹配Mar 15 03:59:59之后的所有行——因为只要行里包含Mar 15 03这个子串就行。如果哪个行里有别的字段恰好包含这个串也会被误伤。解决方法是让匹配模式尽量完整。要么包含到分钟甚至秒要么直接匹配时间戳的前半段带上方括号sed -n /\[15\/Mar\/2025:03:/,/\[15\/Mar\/2025:04:/p access.log另一个实际问题跨天的日志。从31/Mar/2025:23:30切到01/Apr/2025:00:30如果日期不写全sed 的匹配会直接失败或截取错误区间。所以跨天日志要带上完整日期再匹配。4.3 sed 的另一个安全用途日志脱敏再外发除了切窗口sed 还有一手绝活在安全场景里非常有用——脱敏。排查过程中经常要把日志片段发给同事、客户或者贴到工单系统里但日志里可能包含真实 IP、用户名、手机号之类的敏感信息。直接发原日志不妥手工删又费劲这种情况用 sed 做自动替换就很方便。比如把所有 IPv4 地址替换成[IP]sed -E s/([0-9]{1,3}\.){3}[0-9]{1,3}/[IP]/g window.log sanitized.log命令里-E启用扩展正则替换的目标是把形如203.0.113.45的地址整体变成一个占位符。同理可以把邮箱替换成[EMAIL]、把用户名替换成[USER]再外发就放心多了。这个操作看起来很不起眼但在实际工作中很重要。安全分析本身就涉及敏感数据能不能规范地处理日志脱敏体现的是你的职业素养。我见过有人图省事直接发原始日志给外部人员后来惹出麻烦的案例。用 sed 做一层替换成本极低但能避免很多后续风险。4.4 我个人的使用习惯总结我用 sed 的频度不如 grep 和 awk但只要用到基本就是两种情况一是给整个排查流程划定边界先把某段时间的数据准备好二是做数据清洗、字段变形、脱敏。它更像是一个“预处理工具”而不是直接出结论的工具。另外说一句sed -i这种原地修改文件的用法在安全分析时我非常不推荐直接对原始日志执行。日志是证据一旦动了原始文件后续走流程、出报告时很难说清楚。正确做法永远是先拷贝、再处理、保留原始文件不动。这和在运维时改配置前先备份是一个道理。5. sort 和 uniq排序去重把数据变成结论5.1 uniq 有个大前提必须先排序前面几次示例里我都写了sort | uniq -c“这样的组合很多朋友可能没意识到去重计数”必须先经过 sort。uniq这个命令的名字容易误导人它其实只去掉“相邻且重复”的行不是全局去重。换句话说如果重复内容没有连续排列uniq 是看不到的。举个具体的例子。假设某个文件里有11.22.33.44 55.66.77.88 11.22.33.44直接执行uniq -c它会认为三行都不重复因为第二行的 IP 和第三行的 IP 之间隔着一行。只有先sort把相同的 IP 排到一起再执行uniq -c才能得到正确的统计结果sort ip_list.txt | uniq -c我在带新人时见过很多次这种错误花了大半天从日志里提取 IP结果统计完发现同一个 IP 出现了好几行等于前面白干了。这个细节没在真实日志上踩过坑的人很容易忽略。5.2 sort 的排序要分清“字典序”和“数字序”sort 默认是按字典序字符顺序排序的这对数字来说会出问题。比如10会排在2前面因为按字符比较1小于2。这看起来像是常识但真到了统计访问量排名的时候一旦忘了加-n你看到的 TOP 列表会完全不对。正确姿势是sort -rn ip_count.txt这里的-n表示按数字大小排序-r表示逆序从大到小。安全分析里这可能是最常用的一条排序命令把 awk 统计出来的计数从高到低排列谁最可疑一目了然。如果要对日志里的某个字段排序但字段之间用分隔符比如逗号、竖线隔开要指定列sort -t, -k2 -rn data.csv-t,指定分隔符是逗号-k2指定按第二列排序-rn按数字逆序。这在处理导出的业务日志时特别有用。5.3 组合拳一条命令找出 TOP 攻击源现在把所有东西连起来看一个完整的分析任务。某客户反馈有人在刷他们的登录接口现象是短时间内大量POST /api/v1/login请求并且伴随大量失败返回。我先从日志里筛出所有登录接口的请求行grep POST /api/v1/login /var/log/nginx/access.log login.log然后提取客户端 IP 并统计每个 IP 的请求次数awk {print $1} login.log | sort | uniq -c | sort -rn | head -20执行完就能得到一张 20 行的表每行包含一个数字和一个 IP数字代表请求次数。如果前几个 IP 的请求次数是断崖式的比如第一、第二是几千次其余都只有个位数基本可以锁定问题 IP。后续无论是封禁、溯源、还是写报告这个输出就是核心依据。再补充一个技巧用uniq -d只显示重复的行比如从用户登录日志里找同一个 IP 在短时间内多次切换账号的可疑行为。uniq -d配合其他命令在做撞库、批量注册这类分析时非常有用。5.4 把 sort 和 uniq 当“验证工具”而不是“统计工具”很多人把 sort 和 uniq 只当统计工具用我其实更愿意把它们当作验证工具。比如你怀疑某个时间段内有扫描行为你不需要精确统计到底扫了多少次你只需要确认“异常增多”这个事实。用sort | uniq -c把结果列出来观察数据分布的形状比盯着具体数值更有价值。正常流量下每个 IP 的请求次数分布相对平缓。攻击流量下头部几个 IP 的计数会明显异常高。这种“一眼看出不对劲”的能力就是靠 sort 和 uniq 把你从海量日志中拉出来的。6. 实战记录一次完整的安全排查是怎么用这 5 个命令串起来的6.1 告警触发和初步确认某天下午我接到某客户反馈他们对外提供的一个查询接口响应时间从平均 200 毫秒飙升到 5 秒以上并且持续了将近半小时。客户很紧张怀疑是被拖库了。我先登上了他们的 nginx 服务器确认了 access.log 的位置和时间跨度。第一步从日志里抓升高响应时间段的数据用 sed 切出指定时间窗口大概是从 14:10 到 14:40一共 30 分钟。这是一个合理的排查范围告警触发在 14:30往前推 20 分钟是异常开始的时间。sed -n /28\/Mar\/2025:14:10/,/28\/Mar\/2025:14:40/p /var/log/nginx/access.log window.log6.2 先用 grep 做特征筛查锁定可疑路径切出窗口之后我没有急着看全量数据而是先用 grep 做特征筛查。客户业务是查询类接口正常情况请求应该集中在少数几个固定路径上。我先把请求方法为 POST、状态码异常的行过滤出来看个大概grep -E POST.*(sql|select|union|login|api/) window.log | head -50筛出来的结果里立刻出现了几条请求路径和参数里带union select的请求——SQL 注入特征非常明显。但当时我并没有直接下结论因为 head -50 只能看到前 50 行特征出现只要几行不能说明这就是导致响应变慢的原因。这里插一句我的经验攻击特征命中不一定是攻击本身也可能是扫描器在试探。你要看的是“量级”和“频率”。所以我接下来做的不是翻更多行而是统计这些可疑特征在窗口内一共出现了多少次grep -c -E union select|information_schema|sleep\( window.log数量是 300 多对于一个正常业务来说这个数字完全异常。6.3 用 awk 提取字段聚合出可疑 IP 的分布确认“确实有异常流量”之后接下来的问题就是“谁在发这些请求”。这一步用 awk 提取来源 IPawk /union select|information_schema/ {print $1} window.log | sort | uniq -c | sort -rn | head -10输出结果里排第一的 IP 占总可疑请求数的九成以上其余 IP 只有零星几条。这就把“异常流量”缩小到了“单一来源 IP”。这一步做完含义就完全不一样了如果是分散的、来自各地的大量请求可能是社会上的扫描团伙例行扫描国内国外到处都有但集中在一个 IP 上基本可以确认是有人在有针对性地打这个接口。6.4 用 sed 提取攻击 IP 的完整请求记录复原攻击路径锁定 IP 之后我需要把它在这个时间窗口内发的所有请求都理出来看看它到底做了什么。用 grep 再按 IP 筛一遍还是用 sed其实都可以但我更推荐直接 grep 加上下文参数grep -E ^203\.0\.113\.45 window.log attacker.log为什么这里用 grep 而不是 sed因为这个时候我们不再需要“按时间范围”而是“按 IP 过滤”。把每条请求按时间排好之后我用 sed 做了一步清洗把请求行里的时间戳和状态码格式化得更易读方便后续贴到报告里。sed -E s/\[([^]])\].*([A-Z]) ([^]).*([0-9])/\1 \2 \3 \4/ attacker.log这一步纯粹是格式整理让日志从原始的一长串变成更容易阅读的表格。虽然不影响分析结论但对出报告很有帮助。6.5 最终结论和排查报告把整个流程跑完最后得到的结论非常清晰一个固定 IP 在 30 分钟内发送了三百多条带有 SQL 注入特征的请求请求集中在查询接口并伴随大量响应超时。结合接口日志中出现了sleep函数特征可以判断攻击者可能在尝试基于时间盲注。整个过程用时不超过二十分钟没有用到任何商业安全产品就靠这几个命令反复组合。我把这条排查链路总结成了一个小脚本放到自己的工具箱里。下次再遇到类似的接口异常先跑通用分析脚本再按输出结果深入效率提高了很多。7. 常见问题与排查技巧实录7.1 我见过的五个高频错误和对应解法趁这个机会我把日志分析里最常见的问题整理成一张表都是实际操作中踩过的坑希望能帮你少走弯路。常见问题现象原因解决方式uniq 统计结果不准确同一 IP 分多行展示未先 sort 就去重总是写成sort | uniq -c的组合sort 数字排序不对“10”排在“9”前面默认字典序非数字序加-n参数按数字排序grep 匹配 IP 误报多匹配到包含该串的其他地址子串匹配无边界用 (^sed 切窗口区间错误日志截取范围不对匹配锚点不够精确日期和时间带完整再匹配直接在原日志上修改原始证据被破坏直接对原文件操作先cp或重定向到新文件再处理这张表里的每一条我都在真实环境里踩过。有些是刚转安全时犯的错有些是后来带新人时看他们反复犯的错。把它们记下来能省很多时间。7.2 几个提升效率的实战心得除了上面的坑我还有几个已经内化为习惯的做法。第一分析前先看文件大小和时间范围。拿到一个日志文件先ls -lh看大小再head -3看格式再tail -3看最新记录。一看格式就知道字段结构一看大小就知道要不要先切窗口。别上来就直接跑分析命令。第二把常用分析链路沉淀成脚本。比如我把“从 access.log 里找出 TOP 10 来源 IP”这套命令写成了一个函数存到自己的环境配置里每次登录服务器都能直接调用输入日志路径就出结果。这不是什么高深的自动化就是把自己反复做的事情固化下来。第三命令跑完先存输出再人脑分析。很多新人的习惯是看命令行输出的前几行就急着下结论。我现在的习惯是不管结果看起来多明显先把输出重定向到文件再打开文件完整看一遍。因为终端里滚屏会丢掉大量内容很可能前面 20 行正常、后面几百行才是一眼假的东西。用文件保存输出既保留了证据也不容易漏信息。7.3 从运维思维到安全思维差的不是技术最后说一点我在职业转型上最深的体会。运维看日志目标是确认系统处于什么状态安全看日志目标是确认系统是否被攻击、被谁攻击、攻击到什么程度。同样的命令不同的提问方式。有 Linux 基础的人转安全真的是站在一个相对高的起点上。至少你不用从“文件怎么浏览、权限怎么管理”开始学你缺的不是技术栈而是把技术栈迁移到新场景的意识。学会用日志讲一个“攻击故事”你就已经入门了。还有一个对我帮助很大、建议你也试试的习惯每次做完一次排查花十分钟把用过的命令整理成一条流水线注释好每一段是干什么的。存着不吃亏。我自己的这套组合命令脚本就是从最开始的一条条零散命令慢慢沉淀成了一套比较完整的排查模板。等到你手头有了一套自己的模板再遇到突发情况就能从容很多。