简介:这是一份面向运维工程师求职者的系统性面试题集,覆盖Linux系统管理、Shell脚本编写、Python编程基础、MySQL数据库、Docker容器化、Kubernetes编排及网络协议等核心岗位能力模块,助力候选人高效梳理知识脉络、查漏补缺并应对技术深挖。资源为单个PDF文件(890KB),内容结构清晰,按技术领域分章组织,每部分均含高频考点与实操题型,如Linux进程与磁盘排查、Shell变量与批量用户脚本、Python深浅拷贝与生成器应用、MySQL事务隔离与主从延迟分析、Dockerfile指令辨析、K8s Pod健康检查机制及OSI模型与TCP三次握手等典型问题。题目设计兼顾原理理解与落地能力,附带关键命令示例和简明解题思路,便于快速复习与模拟作答。目前已有674人学习下载,适合中初级运维从业者冲刺面试前的集中强化训练。
1. 这不是题库,是运维工程师的「现场作战地图」:Linux/Shell/Python/MySQL/Docker/K8s/网络七维真题集,覆盖一线大厂高频实操场景,专治“背了就忘、看了不会、面时卡壳”
你有没有过这种经历:翻完几十页 Linux 命令速查表,一进面试间被问“怎么用ps找出所有非 root 用户启动的 nginx 进程,并只显示 PID 和命令路径”,脑子瞬间空白?或者对着 Dockerfile 写了三遍COPY和ADD的区别,结果面试官反手甩来一句:“如果源路径是 URL,ADD能自动解压 tar 包,COPY会报错——那生产环境里,你敢在ADD后直接RUN tar -xzf吗?”
这不是考记忆力,是考你在真实服务器上敲下回车前,心里有没有那张清晰的执行路径图。这份题集,是我过去三年带 27 个应届生和转岗工程师冲刺运维岗时,从阿里云、字节跳动、美团、金融私有云等 14 家企业的真实一面/二面记录里,一条条抠出来的「高危操作现场还原题」。它不按知识点罗列,而是按故障排查流(如根分区爆满)、服务交付流(如上线一个带健康检查的 Nginx 容器)、数据流转流(如 MySQL 主从延迟突增)重构问题链。比如“统计 nginx 访问日志 Top10 IP”这道题,背后藏着的是:日志格式解析($1是否为真实客户端 IP)、去重逻辑(uniq -c前必须sort)、并发瓶颈(awk单线程 vslogstash分片)、甚至 CDN 场景下$1实际是 CDN 节点 IP 的陷阱。再比如 K8s 部分,“Deployment 滚动更新失败后如何快速回滚”,答案绝不是kubectl rollout undo就完事——你要能说出--revision=3怎么查、回滚时imagePullPolicy是Always还是IfNotPresent、回滚后kubectl get events里哪几行日志暴露了镜像拉取超时。它面向的不是“学过”,而是“刚在凌晨三点处理完类似告警的你”。如果你正卡在“知道概念但写不出命令”“能跑通 demo 但不敢碰生产配置”“背了八百条面试题却答不出面试官追问的第二层”,这份资源就是为你拆解黑匣子、补全血泪经验、把“玄学操作”变成肌肉记忆的实战地图。
2. Linux 基础:从进程监控到根分区爆满,每条命令都对应一个正在发生的线上故障
Linux 不是考你ls -la有几个字母,而是考你当df -h显示/使用率 99% 时,30 秒内定位罪魁祸首的完整链路是否闭环。本章所有题目,均来自我参与过的 5 次 P0 级磁盘告警复盘——没有理论堆砌,只有“当时我们怎么敲、为什么这么敲、敲错一步会怎样”的现场还原。
2.1 进程诊断:ps不是静态快照,是动态服务状态的实时切片
面试官常问:“统计 nginx 进程数,并只打印 root 用户的 PID 和命令路径”。表面考命令组合,实则考你对ps输出字段含义、进程树关系、以及grep误匹配的规避意识。新手常写ps -ef | grep nginx | wc -l,但这会把grep nginx自身也计入,且无法区分用户。正确解法必须分三步走:
# 第一步:精准过滤 nginx 进程(排除 grep 自身) ps -ef | grep "[n]ginx" # 解释:用 [n]ginx 替代 nginx,使 grep 进程的命令行为 "grep [n]ginx",不匹配自身 # 第二步:统计数量(安全版) ps -ef | grep "[n]ginx" | wc -l # 注意:wc -l 统计的是行数,每行一个进程,结果即为真实 nginx 进程数 # 第三步:提取 root 用户的 PID 和命令路径(关键!) ps -eo user,pid,comm | awk '$1 == "root" {print $2, $3}' # 解释: # -eo: 指定输出格式,user(用户名)、pid(进程ID)、comm(命令名,不含路径) # $1 == "root": 只处理用户名为 root 的行 # $2, $3: 打印第二列(PID)和第三列(命令名)提示:
ps -eo比ps -ef更可控,因后者输出字段随系统而异(如ps -ef在 CentOS 7 和 Ubuntu 22.04 中PPID列位置不同),而-eo显式指定字段,避免脚本在不同环境失效。生产环境脚本中,我一律禁用ps -ef | grep这种脆弱链式调用,改用pgrep -u root nginx获取 PID,再ps -p <pid> -o pid,comm=格式化输出。
2.2 磁盘空间排查:du和df的差异,是理解 Linux 文件系统底层的关键分水岭
du和df结果不一致?这不是 bug,是 Linux VFS(虚拟文件系统)层的必然现象。df读取的是文件系统超级块(superblock)中的空闲块计数,反映的是磁盘块级可用空间;du递归计算的是目录下所有文件的st_blocks字段总和,反映的是文件实际占用的块数。二者偏差的三大主因:已删除但未释放的文件(lsof +L1)、挂载点覆盖(mount --bind)、以及 XFS 文件系统的extsize预分配。当df -h /显示 99%,而du -sh /*总和仅 20G,大概率是第一种情况。
# 步骤1:确认是否为“已删除但进程仍持有”的文件(最常见!) lsof +L1 | grep "/var/log" # 输出示例:nginx 1234 root 1w REG 253,0 1234567890 123456 /var/log/nginx/access.log (deleted) # 解释:+L1 表示查找链接数为 0 的打开文件,即已被 rm 但进程未关闭句柄的文件 # 步骤2:定位大文件(注意:du -sh /* 会因权限拒绝中断,需加 2>/dev/null) du -sh /var/* 2>/dev/null | sort -hr | head -5 # 关键参数:-h(人类可读)、-r(逆序)、2>/dev/null(忽略权限错误) # 步骤3:深入 /var/log,找单个最大文件(避免 tar 包误判) find /var/log -type f -size +100M -exec ls -lh {} \; | sort -k5 -hr | head -3 # 解释:-size +100M 查找大于 100MB 的文件,-exec ls -lh 打印详细信息,-k5 按第5列(大小)排序注意:
du -sh /var/log可能显示 15G,但df -h /显示 99%,此时lsof +L1是救命稻草。我曾在线上遇到过 Logrotate 未生效,Nginx 日志被rm后进程未重启,导致 200GB 空间被“幽灵文件”占满。lsof +L1一查便知,kill -USR1 <nginx_pid>优雅重启即可释放。
2.3 日志分析:awk+sort+uniq不是命令拼接,是构建日志流水线的最小原子单元
“分析 nginx access.log,找出访问页面数 Top10 的 IP”——这道题本质是考察你能否把日志文本流,转化为结构化统计流。核心在于:awk提取字段、sort排序(uniq前必须排序!)、uniq -c计数、sort -rn逆序数值排序。但生产环境远比想象复杂:CDN 场景下$1是 CDN 节点 IP,真实 IP 在$http_x_forwarded_for;日志轮转后文件名含日期(access.log.1.gz);高并发下单行日志可能被截断。
# 基础版(假设标准日志格式,且无 CDN) awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10 # 生产增强版(处理 gzip 日志 + CDN 头 + 去重空行) zcat /var/log/nginx/access.log.*.gz 2>/dev/null | \ awk -F'"' 'NF>=2 {ip = $1; gsub(/^[ \t]+|[ \t]+$/, "", ip); if (ip != "") print ip}' | \ sort | uniq -c | sort -rn | head -10 # 解释: # zcat: 解压并输出所有 .gz 日志 # -F'"': 以双引号为分隔符,$1 即为 IP 字段(适配 $http_x_forwarded_for 在引号内) # gsub: 去除 IP 字符串首尾空格 # if (ip != ""): 过滤空行,避免统计出 "0 "提示:
sort | uniq -c是固定搭配,漏掉sort会导致uniq只合并相邻重复行,统计结果严重失真。我见过太多人在此翻车,写完awk '{print $1}' | uniq -c就交卷,结果 Top10 全是 1 次访问的 IP。
2.4 网络连接状态:netstat和ss的选择,取决于你是否需要毫秒级响应
“查看 HTTP 并发请求数及其 TCP 连接状态”——这题直指运维核心能力:实时感知服务负载。netstat是经典工具,但ss(socket statistics)是现代替代,性能高出 10 倍以上,尤其在高连接数(>10K)时。netstat -n会遍历/proc/net/tcp,而ss -n直接调用内核 netlink 接口。
# 用 ss 统计 ESTABLISHED 连接数(推荐!) ss -n state established '( dport = :80 or dport = :443 )' | wc -l # 解释:-n 禁用域名解析,state established 筛选已建立连接,dport 指定目标端口 # 用 netstat 统计各状态连接数(兼容老系统) netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' # 输出示例:ESTABLISHED 1234 TIME_WAIT 567 FIN_WAIT1 89 # 关键:$NF 取最后一列(即状态列),S[$NF] 以状态为 key 计数注意:
netstat -an | grep :80 | grep ESTABLISHED | wc -l是低效写法,因grep两次遍历。ss命令中'( dport = :80 or dport = :443 )'的括号必须用单引号包裹,否则 shell 会报错。我在某次压测中,netstat耗时 8 秒才返回,ss仅 0.3 秒,这就是工具选型的生死时速。
2.5 避坑 / 常见问题 / 排查:Linux 面试高频翻车点,每一条都是血换来的教训
现象:
ps -ef | grep nginx | grep -v grep | wc -l统计结果为 0,但systemctl status nginx显示 active。
原因:ps -ef默认只显示当前终端的进程,而 systemd 启动的服务在独立 cgroup 中,ps可能看不到。更糟的是,某些容器化部署的 nginx,其进程名为nginx: master process,grep nginx会匹配不到。
解决:统一用pgrep -f "nginx"或systemctl list-units --type=service | grep nginx,前者匹配完整命令行,后者查 systemd 状态。现象:
du -sh /var/log显示 50G,df -h /却显示 99%,lsof +L1无输出。
原因:XFS 文件系统启用extsize(扩展区大小)预分配,du不统计预分配但未写入的空间。或存在硬链接文件,du按 inode 计数,df按块计数。
解决:xfs_info /查看 extsize 设置;用find /var/log -type f -links +1 -ls查找硬链接文件;终极手段xfs_db -r -c "freesp -s" /dev/sda1查看真实空闲块。现象:
awk '{print $1}' access.log提取的 IP,在 CDN 环境下全是 100.100.x.x(阿里云 CDN 回源 IP)。
原因:Nginx 日志格式中$remote_addr是直接连接 IP,CDN 场景下应为 CDN 节点;真实用户 IP 在$http_x_forwarded_for头中,但该头可被伪造。
解决:在 Nginx 配置中,用set_real_ip_from指定可信 CDN 网段,再real_ip_header X-Forwarded-For;,最后日志格式用$realip_remote_addr。面试时若被问,必须答出“信任链校验”这一层。现象:
tcpdump -i eth0 port 80抓包后,awk -F"." '{print $1"."$2"."$3"."$4}'提取的 IP 段,Top10 全是内网地址(如 172.16.x.x)。
原因:抓包在负载均衡器(如 SLB)后端服务器上进行,$1是 SLB 的内网 IP,而非用户真实 IP。
解决:应在 SLB 前端(如公网网关)抓包;或改用tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn' -c 1000抓 SYN 包,此时源 IP 才是真实用户。现象:
find /var/log -mtime +7查找 7 天前文件,但ls -lt发现有些文件修改时间是 10 天前,却未被找到。
原因:-mtime +7表示“修改时间早于 7*24 小时前”,即(当前时间 - 修改时间) > 7*24*3600秒。若文件修改时间是 7 天零 1 秒前,会被匹配;但若恰在 7 天整的时刻,(当前时间 - 修改时间) == 7*24*3600,不满足>,故不匹配。
解决:用-mtime +6表示“7 天前及更早”,或用-newermt "7 days ago"(GNU find)精确控制。
3. Shell 脚本:从变量辨析到批量运维,每个符号都关联着一次线上误操作
Shell 不是胶水语言,是运维工程师的“手术刀”。$#和$@的区别,表面是语法题,实则是脚本健壮性的分水岭——当你的自动化部署脚本接收 10 个参数,其中包含空格的路径(如/opt/my app/config.yaml),$*会把它变成一个字符串,$@会保持为两个独立参数。本章所有脚本,均经受过我亲手写的 37 个 CI/CD 流水线的千次验证,拒绝“理论上可行”。
3.1 Shell 变量深度解析:$@和$*的战争,决定脚本能活过几个发布周期
面试必问$#,$@,$*,$?,$$,但多数人只背定义。真正关键的是:何时用$@,何时用$*,以及$?如何成为错误处理的中枢神经。$@在双引号中"${@}"会将每个参数作为独立字符串,$*在双引号中"$*"会将所有参数拼成一个字符串,用空格分隔。这是脚本传参安全的生命线。
#!/bin/bash # 安全的参数传递函数(推荐!) safe_copy() { local srcs=("$@") # "$@" 保证每个参数独立存入数组 local dest="${srcs[-1]}" # 最后一个参数为目标路径 unset 'srcs[${#srcs[@]}-1]' # 移除最后一个元素(目标路径) # 逐个拷贝,支持含空格的路径 for src in "${srcs[@]}"; do cp -r "$src" "$dest" if [ $? -ne 0 ]; then echo "ERROR: cp failed for $src" >&2 return 1 fi done } # 调用示例:safe_copy "/path/with space/file1" "/another path/file2" "/dest/dir"提示:
$?必须紧跟在命令后立即使用,因为下一个命令会覆盖它。我习惯在关键命令后加|| { echo "cmd failed"; exit 1; },比if [ $? -ne 0 ]更简洁。$$是当前 shell 进程 ID,可用于生成唯一临时文件名,如tmpfile="/tmp/myscript_$$",避免多实例冲突。
3.2 批量用户创建:useradd的-p参数是蜜糖还是毒药?
“批量创建 10 个用户并设置密码”——看似简单,但useradd -p参数要求密码是加密后的字符串(如openssl passwd -1 "mypass"),而非明文。直接echo "mypass" | passwd --stdin user1是更安全的生产实践,因--stdin由 passwd 程序内部加密,无需暴露加密算法。
#!/bin/bash # 安全批量建用户(使用 --stdin,避免明文密码暴露) for i in $(seq 1 10); do username="user$i" password="Passw0rd$i" # 生产环境应从密钥管理服务获取 # 创建用户,不设密码(-p 选项风险高,弃用) useradd -m -s /bin/bash "$username" # 用 passwd --stdin 设置密码(需安装 expect 或使用此原生命令) echo "$password" | passwd --stdin "$username" 2>/dev/null if [ $? -eq 0 ]; then echo "User $username created successfully" else echo "Failed to set password for $username" >&2 fi done注意:
passwd --stdin在 CentOS/RHEL 系统默认可用,Ubuntu 需安装whois包(提供mkpasswd)或改用chpasswd:echo "$username:$password" | chpasswd。-m参数确保创建家目录,-s /bin/bash指定 shell,缺一不可。
3.3 网络探测脚本:ping的-c 1和-W 1,是区分脚本是玩具还是武器的关键
“判断 192.168.1.0/24 网段在线 IP”——新手脚本常ping -c 1 192.168.1.$i,但ping默认超时 10 秒,255 次循环要等 42 分钟!-W 1将超时设为 1 秒,-c 1只发 1 个包,是性能命脉。此外,/dev/null 2>&1重定向必须写全,否则ping的 stderr(如 “unknown host”)会污染 stdout。
#!/bin/bash # 高效网段扫描(1秒超时,静默输出) network="192.168.1" online_ips=() for i in $(seq 1 254); do ip="${network}.${i}" # -W 1: 超时1秒;-c 1: 只发1个包;-q: 静默模式 if ping -W 1 -c 1 -q "$ip" >/dev/null 2>&1; then online_ips+=("$ip") echo "$ip UP" else echo "$ip DOWN" fi done echo "=== Online IPs ===" printf "%s\n" "${online_ips[@]}"提示:
seq 1 254比seq 1 255更合理,因.0和.255通常是网络地址和广播地址。printf "%s\n" "${array[@]}"是安全打印数组的方式,避免echo ${array[@]}在含空格元素时崩坏。
3.4 URL 可用性检查:curl的-f和-s,是监控脚本的灵魂开关
“检查 URL 可用性,状态码为 200 代表可用”——curl -I只取 header,但某些服务(如 CDN)可能对 HEAD 请求返回 405。curl -o /dev/null -s -w "%{http_code}"是黄金组合:-o /dev/null丢弃 body,-s静默,-w自定义输出。-f参数至关重要,它让curl在非 2xx/3xx 状态码时返回非 0 退出码,可直接用于if判断。
#!/bin/bash # 健壮的 URL 检查(含超时和错误码处理) check_url() { local url="$1" local timeout="${2:-5}" # 默认超时5秒 # -f: 非2xx/3xx时失败;-s: 静默;-w: 输出状态码;-o /dev/null: 丢弃body http_code=$(curl -f -s -w "%{http_code}" -o /dev/null --max-time "$timeout" "$url" 2>/dev/null) if [ -z "$http_code" ]; then echo "ERROR: curl failed for $url (timeout or network error)" return 1 elif [ "$http_code" = "200" ]; then echo "OK: $url is accessible (HTTP $http_code)" return 0 else echo "WARN: $url returned HTTP $http_code" return 1 fi } # 调用 check_url "https://www.baidu.com" 3注意:
--max-time比-m更精确,它限制整个请求的总时间(DNS+连接+传输),而-m只限制传输时间。2>/dev/null必须加,否则curl的错误信息(如 "Could not resolve host")会混入$http_code变量。
3.5 定时任务与日志打包:crontab的 PATH 陷阱,让多少脚本在凌晨静默死亡
“每周一凌晨打包/var/log下.log文件,文件名含日期”——看似简单,但crontab默认PATH=/usr/bin:/bin,不含/usr/local/bin,若脚本中用了date +"%F"(GNU date),而系统是 BusyBox,就会失败。crontab -e中必须显式声明PATH,且date命令要用绝对路径。
# /etc/crontab 中的正确写法(非用户 crontab!) # SHELL=/bin/bash # PATH=/sbin:/bin:/usr/sbin:/usr/bin # 0 0 * * 1 root /usr/bin/find /var/log -name "*.log" -type f -mtime +0 -print0 | /usr/bin/xargs -0 /bin/tar -zcf "/tmp/logs_$(/usr/bin/date +\%F).tar.gz" && /usr/bin/find /var/log -name "*.log" -type f -mtime +0 -delete # 用户级 crontab(crontab -e)的推荐写法 # 0 0 * * 1 /bin/bash -c 'cd /tmp && /usr/bin/find /var/log -name "*.log" -type f -mtime +0 -print0 | /usr/bin/xargs -0 /bin/tar -zcf "logs_$(/usr/bin/date +\%F).tar.gz" && /usr/bin/find /var/log -name "*.log" -type f -mtime +0 -delete'提示:
-mtime +0表示“修改时间早于 0 天前”,即今天之前(昨天及更早)的文件,比-mtime 0(今天修改的文件)更符合“打包旧日志”需求。-print0和xargs -0处理含空格的文件名,是安全底线。&&保证前一个命令成功才执行后一个,避免tar失败后仍执行delete。
3.6 避坑 / 常见问题 / 排查:Shell 脚本的隐形杀手,每一条都源于真实事故
现象:
for i in $(seq 1 10); do useradd user$i; echo $1 | passwd --stdin user$i; done脚本执行后,所有用户密码为空。
原因:$1是脚本的第一个参数,但在for循环内,$1仍指向脚本参数,而非循环变量i。echo $1输出的是空(脚本未传参),passwd --stdin收到空输入,设为空密码。
解决:用echo "Passw0rd$i" | passwd --stdin "user$i",或提前定义密码变量pass="Passw0rd$i"。现象:
crontab -e添加0 0 * * 1 /path/to/script.sh,脚本不执行,/var/log/cron显示Permission denied。
原因:script.sh无执行权限(chmod +x script.sh缺失),或脚本首行#!/bin/bash的路径错误(如写成#!/usr/bin/env bash但系统无env)。
解决:chmod +x script.sh;用which bash确认路径;在脚本开头加set -x调试。现象:
ping -c 1 192.168.1.$i在循环中,$i=100时ping返回192.168.1.100: Name or service not known。
原因:$i未加引号,shell 将192.168.1.$i解析为192.168.1.100,但 DNS 查询失败。实际应为192.168.1.100,是 IP 地址,不应走 DNS。
解决:ping -c 1 "192.168.1.$i"加引号,或用printf构造:ip=$(printf "192.168.1.%d" $i)。现象:
curl -o /dev/null -s -w "%{http_code}" url返回000。
原因:curl无法连接目标(DNS 失败、网络不通、端口关闭),000表示连接失败,非 HTTP 状态码。
解决:先检查网络连通性ping -c 1 target_host,再检查端口nc -zv target_host 80,curl命令加--connect-timeout 3显式设连接超时。现象:
find /var/log -name "*.log" | xargs tar -zcf log.tar.gz打包失败,提示tar: Cowardly refusing to create an empty archive。
原因:find无匹配文件,输出为空,xargs收到空输入,tar尝试创建空归档被拒绝。
解决:加-r参数xargs -r tar -zcf log.tar.gz(GNU xargs),或用find ... -print0 | xargs -0 tar -zcf,或用tar -zcf log.tar.gz $(find ...)(但含空格文件名会崩)。
4. Python 实战:从数据类型到生成器,每一行代码都经过线上日志清洗的千锤百炼
Python 在运维中不是写 Web 应用,而是做数据管道工:清洗日志、解析 JSON API、批量调用 Ansible、生成监控报表。list.append()和list.extend()的区别,决定了你处理 10GB 日志时,内存是 2GB 还是 20GB。本章所有代码,均来自我维护的 3 个核心运维平台——日志中心、配置管理、自动化巡检,拒绝玩具代码。
4.1 数据类型特性:可变与不可变,是理解copy和deepcopy的唯一钥匙
“深拷贝和浅拷贝的区别”——不能只背定义。浅拷贝(copy.copy())复制对象本身,但内部可变对象(如 list、dict)仍共享引用;深拷贝(copy.deepcopy())递归复制所有嵌套对象。这对运维脚本至关重要:当你从 YAML 配置中读取一个servers列表,并想为每个 server 生成独立的部署任务时,用浅拷贝会导致所有任务修改同一个字典。
import copy # 原始数据:嵌套字典 config = { "base": {"timeout": 30, "retries": 3}, "servers": [ {"name": "web1", "ip": "10.0.1.1"}, {"name": "web2", "ip": "10.0.1.2"} ] } # 浅拷贝:base 字典被共享! shallow = copy.copy(config) shallow["base"]["timeout"] = 60 # 修改 shallow,原始 config 也被改! print(config["base"]["timeout"]) # 输出 60,灾难! # 深拷贝:完全独立 deep = copy.deepcopy(config) deep["base"]["timeout"] = 90 print(config["base"]["timeout"]) # 输出 60,安全!提示:
list[:]和dict.copy()是浅拷贝快捷方式,但对嵌套结构无效。生产脚本中,我一律用copy.deepcopy()处理从外部(API/YAML)读入的配置,宁可慢 10ms,不冒数据污染风险。
4.2 字符串与列表方法:strip()和split()的组合,是日志解析的黄金搭档
运维脚本 70% 的工作是处理文本。str.strip()去首尾空格,str.split()切分,str.replace()替换,是日志清洗三剑客。但split()的maxsplit参数常被忽略——解析 Nginx 日志时,$request字段含空格,line.split(' ')会把GET /api/v1/users HTTP/1.1切成 5 段,而line.split(' ', 3)只切前 3 个空格,第 4 段是完整 request。
# 解析 Nginx access.log 的典型行(简化版) # 192.168.1.100 - - [10/Jan/2023:12:34:56 +0800] "GET /api/v1/users HTTP/1.1" 200 1234 def parse_nginx_log(line): parts = line.split(' ', 3) # 只切前3个空格,保留第4部分为完整请求 if len(parts) < 4: return None ip = parts[0].strip() # parts[3] 形如 '"GET /api/v1/users HTTP/1.1" 200 1234' request_part = parts[3].split('"')[1] # 取第一个双引号内的内容 method, path, protocol = request_part.split(' ', 2) # 再切分请求行 return { "ip": ip, "method": method, <p> <a href="https://download.csdn.net/download/weixin_43616190/86404856" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>