Linux 日志处理三剑客:grep、awk、sed

📚 命令概述

🔍 grep - 全局正则表达式打印工具

命令全称 : Global Regular Expression Print
核心作用 : 在文件中搜索匹配指定模式的文本行
主要用途:

  • 查找包含特定关键字的日志行
  • 过滤日志内容
  • 统计匹配行数
  • 实时监控日志中的错误信息

一句话总结: grep 是"查找器",用于快速定位你想要的内容


📊 awk - 文本分析工具

命令全称 : Aho, Weinberger, Kernighan(三位创始人名字首字母)
核心作用 : 按行处理文本,按列提取和分析数据
主要用途:

  • 提取日志中的特定字段(如时间、IP、状态码)
  • 统计和计算(如求和、平均值、计数)
  • 条件过滤和格式化输出
  • 复杂的文本数据分析

一句话总结: awk 是"分析器",擅长处理结构化数据和统计计算


✏️ sed - 流编辑器

命令全称 : Stream Editor
核心作用 : 对文本进行批量编辑和转换
主要用途:

  • 替换文本内容
  • 删除特定行
  • 插入和追加内容
  • 提取指定行
  • 批量修改文件

一句话总结: sed 是"编辑器",用于批量修改和处理文本

📖 详细用法介绍

一、grep - 文本搜索命令

🎯 grep 是什么?

grep 是 Linux 中最常用的文本搜索工具,它能在文件中查找包含指定模式的行,并将这些行输出。就像在书中用荧光笔标记关键字一样,grep 帮你快速找到日志中的重要信息。

💡 为什么需要 grep?

在生产环境中,日志文件通常有几十万甚至上百万行,手动查找几乎不可能。grep 可以在几秒内从海量日志中找到你需要的错误信息、异常堆栈或特定事件。


📋 grep 常见使用场景

场景一:查看完整异常堆栈

问题: Java 异常堆栈通常是多行的,只看一行看不到完整信息

解决方案:

perl 复制代码
# 显示匹配行及其后 50 行
grep -A 50 "java.lang.NullPointerException" a.log


# 结合 less 分页查看
grep -A 50 "java.lang.NullPointerException" a.log | less

要点说明:

  • -A N 参数:显示匹配行之后的 N 行(After)

  • 异常堆栈通常需要看后续 20-50 行才能找到根本原因

  • less 命令用于分页查看:

    • ↑↓ 键滚动
    • G 跳到末尾
    • q 退出

场景二:实时监控新日志

问题: 需要实时观察线上服务是否出现错误

解决方案:

bash 复制代码
# 实时监控并显示异常及后续 50 行
tail -f a.log | grep -A 50 "java.lang.NullPointerException"


# 忽略大小写实时监控
tail -f a.log | grep -i -A 50 "error"

要点说明:

  • tail -f:实时追踪文件新增内容
  • 结合 grep 过滤关键异常
  • Ctrl + C 停止监控
  • -i 参数忽略大小写

场景三:查找历史/压缩日志

问题: 需要在多个日志文件或压缩日志中查找问题

查找所有 .log 文件:

bash 复制代码
# -H 参数显示文件名
grep -H -A 50 "java.lang.NullPointerException" *.log

查找压缩日志 (.gz)​:

arduino 复制代码
# zgrep 直接处理 .gz 文件,无需解压
zgrep -H -A 50 "java.lang.NullPointerException" *.gz

要点说明:

  • -H 参数:显示匹配内容所在的文件名
  • zgrep:专门处理压缩文件,无需手动解压
  • *.log*.gz:通配符匹配所有相关文件

场景四:统计异常出现次数

问题: 判断异常是偶发还是频繁发生

解决方案:

perl 复制代码
# 统计单个文件
grep -c "java.lang.NullPointerException" a.log


# 统计所有日志文件
grep -c "java.lang.NullPointerException" *.log

输出示例:

c 复制代码
app.log:15
app.log.1:3
app.log.2:0

要点说明:

  • -c 参数:只显示匹配行的数量,不显示内容
  • 快速判断问题严重程度

📊 grep 参数速查表

| 参数 | 含义 | 作用 | 示例 |
|--------|----------------|------------------|--------------------------|-------------|
| -A N | After | 显示匹配行之后的 N 行 | grep -A 10 "error" log |
| -B N | Before | 显示匹配行之前的 N 行 | grep -B 5 "error" log |
| -C N | Context | 显示匹配行上下各 N 行 | grep -C 25 "error" log |
| -i | ignore case | 忽略大小写 | grep -i "ERROR" log |
| -v | invert | 反向匹配(不包含) | grep -v "DEBUG" log |
| -H | with filename | 显示匹配的文件名 | grep -H "error" *.log |
| -h | no filename | 不显示文件名 | grep -h "error" *.log |
| -c | count | 统计匹配行数 | grep -c "error" log |
| -n | line number | 显示行号 | grep -n "error" log |
| -r | recursive | 递归搜索目录 | grep -r "error" /logs/ |
| -E | extended regex | 使用扩展正则 | `grep -E "error | warn" log` |
| -w | word | 完整单词匹配 | grep -w "error" log |


💡 grep 实战技巧

  1. 看异常必须用 -A - 单行看不到完整堆栈信息
  2. 实时监控用 tail -f | grep - 第一时间发现问题
  3. 压缩日志用 zgrep - 省时省力,无需解压
  4. 批量查找加 -H - 知道是哪个文件出的问题
  5. 统计频率用 -c - 判断问题严重程度
  6. 组合使用 -A -B -C - 查看完整上下文

二、awk - 文本分析命令

🎯 awk 是什么?

awk 是一个强大的文本分析工具,它把每一行看作一条记录,把每一列看作一个字段。就像 Excel 处理表格数据一样,awk 可以提取、计算、统计日志中的结构化数据。

💡 为什么需要 awk?

日志通常是结构化的(如:时间 | IP | 状态码 | 响应时间),当你需要:

  • 只看某几列数据
  • 统计某个字段的总和或平均值
  • 根据条件过滤数据
  • 进行复杂的数据分析

这时 awk 就是最佳选择!


📋 awk 基本概念

awk 如何看待文本:

bash 复制代码
2025-12-19 10:30:15 192.168.1.100 GET /api/user 200 150ms
    $1         $2        $3        $4    $5     $6  $7
   字段1     字段2     字段3    字段4  字段5  字段6 字段7

awk 的工作流程:

  1. 读取一行
  2. 按分隔符(默认空格/tab)分割成字段
  3. 对字段进行处理
  4. 输出结果
  5. 继续下一行

📋 awk 常见使用场景

场景一:提取特定列

问题: 只想看日志中的时间和错误信息

解决方案:

dart 复制代码
# 提取第1列(时间)和第5列(错误信息)
awk '{print $1, $5}' app.log


# 提取包含ERROR的行的特定字段
awk '/ERROR/ {print $1, $2, $NF}' app.log


# 提取第一列和最后一列
awk '{print $1, $NF}' app.log

输出示例:

yaml 复制代码
2025-12-19 NullPointerException
2025-12-19 TimeoutException

要点说明:

  • $1, $2, $3...:表示第1、2、3列
  • $NF:表示最后一列(Number of Fields)
  • $0:表示整行内容

场景二:统计分析

问题: 统计各状态码出现次数、计算平均响应时间

统计状态码出现次数:

css 复制代码
# 假设状态码在第9列
awk '{count[$9]++} END {for(code in count) print code, count[code]}' access.log

输出示例:

复制代码
200 15234
404 89
500 23

计算响应时间统计:

makefile 复制代码
# 假设响应时间在第10列
awk '{sum+=$10; count++} END {print "总计:", sum, "平均:", sum/count}' access.log

输出示例:

makefile 复制代码
总计: 125000 平均: 250

统计 IP 访问次数 TOP 10:

bash 复制代码
# 第1列是IP
awk '{ip[$1]++} END {for(i in ip) print ip[i], i}' access.log | sort -rn | head -10

输出示例:

yaml 复制代码
1523 192.168.1.100
892 192.168.1.101
456 192.168.1.102

要点说明:

  • count[$9]++:使用关联数组统计
  • END {}:所有行处理完后执行
  • sum+=:累加计算

场景三:条件过滤

问题: 只看响应时间超过1秒的请求

解决方案:

dart 复制代码
# 显示响应时间大于1000ms的请求
awk '$10 > 1000 {print $0}' access.log


# 显示特定时间段的日志
awk '$1 >= "2025-12-19 10:00:00" && $1 <= "2025-12-19 11:00:00"' app.log


# 显示状态码为5xx的错误
awk '$9 ~ /^5/ {print $0}' access.log


# 显示状态码不是200的请求
awk '$9 != 200 {print $0}' access.log

要点说明:

  • $10 > 1000:数值比较
  • $1 >= "xxx" && $1 <= "yyy":字符串比较和逻辑运算
  • $9 ~ /^5/:正则匹配(以5开头)
  • !=:不等于

场景四:格式化输出

问题: 让输出更易读,添加表头和格式

解决方案:

swift 复制代码
# 格式化输出,添加表头
awk 'BEGIN {print "时间\t\t\t状态码\t响应时间"} 
     {printf "%s\t%s\t%s\n", $1, $9, $10}' access.log


# 计算并格式化输出百分比
awk '{total++; if($9=="200") success++} 
     END {printf "成功率: %.2f%%\n", (success/total)*100}' access.log


# 添加行号
awk '{print NR, $0}' app.log

输出示例:

yaml 复制代码
时间			状态码	响应时间
2025-12-19	200	150
2025-12-19	404	50
成功率: 98.50%

要点说明:

  • BEGIN {}:处理第一行之前执行
  • printf:格式化输出(类似C语言)
  • %.2f:保留两位小数
  • NR:当前行号

📊 awk 内置变量速查表

变量 含义 说明 示例
$0 整行内容 当前处理的整行 awk '{print $0}'
$1, $2, $n 第n个字段 按分隔符分割的字段 awk '{print $1, $3}'
$NF 最后一个字段 Number of Fields awk '{print $NF}'
NR 行号 Number of Records awk 'NR==10 {print $0}'
NF 字段数 当前行有多少列 awk '{print NF}'
FS 字段分隔符 Field Separator(默认空格) awk 'BEGIN{FS=","}'
OFS 输出字段分隔符 Output Field Separator awk 'BEGIN{OFS="\t"}'

📊 awk 常用参数和操作符

| 参数/操作符 | 作用 | 示例 |
|-------------------|---------|---------------------------|-----|----------------|---|------------|
| -F | 指定字段分隔符 | awk -F',' '{print $1}' |
| ~ | 正则匹配 | awk '$0 ~ /ERROR/' |
| !~ | 正则不匹配 | awk '$0 !~ /DEBUG/' |
| == | 等于 | awk '$1 == "200"' |
| != | 不等于 | awk '$1 != "200"' |
| > < >= <= | 大小比较 | awk '$10 > 1000' |
| && | 逻辑与 | awk '$1==200 && $2>100' |
| ` | | ` | 逻辑或 | `awk '1==200 | | 1==201'` |


💡 awk 实战技巧

  1. 指定分隔符 : 日志不是空格分隔时用 -F

    bash 复制代码
    awk -F',' '{print $1}'  # CSV文件
    awk -F':' '{print $1}'  # 冒号分隔
  2. 多条件过滤 : 使用 &&||

    dart 复制代码
    awk '$9==200 && $10>1000 {print $0}'
  3. 统计去重: 使用关联数组

    scss 复制代码
    awk '{arr[$1]++} END {print length(arr)}'  # 统计唯一IP数
  4. BEGIN 和 END:

    • BEGIN:处理数据前执行(设置变量、打印表头)
    • END:处理数据后执行(打印统计结果)
  5. 格式化输出 : 使用 printf 而不是 print

    swift 复制代码
    awk '{printf "%-20s %10d\n", $1, $2}'

三、sed - 流编辑命令

🎯 sed 是什么?

sed 是一个流编辑器(Stream Editor),它可以对文本进行批量编辑操作。就像 Word 的"查找替换"功能,但 sed 更强大,可以处理整个文件或多个文件,支持正则表达式,还能删除、插入、提取行。

💡 为什么需要 sed?

当你需要:

  • 批量替换日志中的敏感信息
  • 删除调试日志(DEBUG 行)
  • 提取特定行号或匹配的行
  • 在特定位置插入内容
  • 不打开文件就能修改内容

这时 sed 就是最佳工具!


📋 sed 基本概念

sed 的工作流程:

  1. 读取一行到模式空间(Pattern Space)
  2. 执行编辑命令
  3. 输出结果到标准输出
  4. 继续下一行

sed 命令格式:

arduino 复制代码
sed [选项] '命令' 文件
sed [选项] -e '命令1' -e '命令2' 文件

📋 sed 常见使用场景

场景一:内容替换(最常用)​

问题: 需要批量替换日志中的文本

基本替换:

c 复制代码
# 替换每行第一个ERROR为[错误]
sed 's/ERROR/[错误]/' app.log


# 全局替换(替换所有,不只是第一个)
sed 's/ERROR/[错误]/g' app.log


# 替换并保存到新文件
sed 's/ERROR/[错误]/g' app.log > app_new.log


# 直接修改原文件(谨慎使用!)
sed -i 's/ERROR/[错误]/g' app.log


# 修改前备份原文件
sed -i.bak 's/ERROR/[错误]/g' app.log

高级替换:

bash 复制代码
# 只替换包含特定模式的行
sed '/2025-12-19/s/ERROR/[错误]/g' app.log


# 替换特定行号(第10行)
sed '10s/ERROR/[错误]/g' app.log


# 替换行号范围(10-20行)
sed '10,20s/ERROR/[错误]/g' app.log


# 忽略大小写替换
sed 's/error/[错误]/gi' app.log

要点说明:

  • s/old/new/:替换命令格式
  • g:全局替换(global)
  • i:忽略大小写(ignore case)
  • -i:直接修改文件(in-place)

场景二:删除行

问题: 删除日志中不需要的内容

解决方案:

bash 复制代码
# 删除空行
sed '/^$/d' app.log


# 删除包含DEBUG的行
sed '/DEBUG/d' app.log


# 删除第1-10行
sed '1,10d' app.log


# 删除最后一行
sed '$d' app.log


# 删除第5行
sed '5d' app.log


# 删除第3行到最后一行
sed '3,$d' app.log


# 删除不包含ERROR的行(保留ERROR行)
sed '/ERROR/!d' app.log

组合删除:

bash 复制代码
# 删除空行和DEBUG行
sed -e '/^$/d' -e '/DEBUG/d' app.log


# 或使用分号
sed '/^$/d; /DEBUG/d' app.log

要点说明:

  • d:删除命令(delete)
  • ^$:正则表达式,表示空行
  • !d:反向操作,删除不匹配的行

场景三:提取特定行

问题: 只想看日志中的某些行

解决方案:

bash 复制代码
# 只显示包含ERROR的行(类似grep)
sed -n '/ERROR/p' app.log


# 显示第10-20行
sed -n '10,20p' app.log


# 显示第一行
sed -n '1p' app.log


# 显示最后一行
sed -n '$p' app.log


# 显示第1行和最后一行
sed -n '1p;$p' app.log


# 显示匹配行及其后5行
sed -n '/ERROR/,+5p' app.log


# 显示从匹配行到文件末尾
sed -n '/ERROR/,$p' app.log


# 显示两个模式之间的内容
sed -n '/开始标记/,/结束标记/p' app.log

要点说明:

  • -n:静默模式,不自动打印
  • p:打印命令(print)
  • ,+5:当前行及后5行
  • 模式1,模式2:从模式1到模式2之间的行

场景四:插入和追加

问题: 在特定位置添加内容

解决方案:

bash 复制代码
# 在匹配行前插入内容
sed '/ERROR/i ====== 发现错误 ======' app.log


# 在匹配行后追加内容
sed '/ERROR/a ====== 错误结束 ======' app.log


# 在文件开头插入
sed '1i 日志开始时间: 2025-12-19' app.log


# 在文件末尾追加
sed '$a 日志结束' app.log


# 在第10行前插入
sed '10i 这是插入的内容' app.log


# 插入多行(使用\n)
sed '/ERROR/i ======\n错误详情\n======' app.log

要点说明:

  • i:在匹配行前插入(insert)
  • a:在匹配行后追加(append)
  • $:最后一行

场景五:多个操作组合

问题: 需要同时进行多种编辑操作

解决方案:

ini 复制代码
# 方法1:使用多个 -e
sed -e '/^$/d' -e 's/ERROR/[错误]/g' -e '/DEBUG/d' app.log


# 方法2:使用分号分隔
sed '/^$/d; s/ERROR/[错误]/g; /DEBUG/d' app.log


# 方法3:使用多行(可读性更好)
sed '
/^$/d
s/ERROR/[错误]/g
/DEBUG/d
' app.log


# 复杂处理示例
sed -n '
/ERROR/!d          # 只保留ERROR行
s/ERROR/[错误]/g   # 替换ERROR
s/WARN/[警告]/g    # 替换WARN
p                  # 打印结果
' app.log

📊 sed 命令速查表

命令 作用 示例
s/old/new/ 替换(substitute) sed 's/ERROR/错误/'
d 删除(delete) sed '/DEBUG/d'
p 打印(print) sed -n '/ERROR/p'
i 插入(insert) sed '/ERROR/i 标记'
a 追加(append) sed '/ERROR/a 标记'
c 替换整行(change) sed '/ERROR/c 错误行'
y 字符转换 sed 'y/abc/ABC/'
q 退出(quit) sed '10q'

📊 sed 参数速查表

参数 作用 示例
-n 静默模式,不自动打印 sed -n '/ERROR/p'
-e 执行多个编辑命令 sed -e 's/a/b/' -e 's/c/d/'
-i 直接修改文件 sed -i 's/old/new/g' file
-i.bak 修改前备份 sed -i.bak 's/old/new/g' file
-r 使用扩展正则 sed -r 's/[0-9]+/NUM/g'
-f 从文件读取命令 sed -f script.sed file

📊 sed 地址范围

地址 含义 示例
n 第n行 sed '5d' 删除第5行
n,m 第n到m行 sed '10,20d' 删除10-20行
n,+m 第n行及后m行 sed '10,+5d' 删除10行及后5行
n~m 从n开始每m行 sed '1~2d' 删除奇数行
$ 最后一行 sed '$d' 删除最后一行
/pattern/ 匹配的行 sed '/ERROR/d' 删除ERROR行
/p1/,/p2/ 从p1到p2 sed '/开始/,/结束/d'

💡 sed 实战技巧

  1. 先测试再修改 : 去掉 -i 先看效果

    bash 复制代码
    sed 's/ERROR/错误/g' app.log  # 先预览
    sed -i 's/ERROR/错误/g' app.log  # 确认后再修改
  2. 备份原文件 : 使用 -i.bak

    c 复制代码
    sed -i.bak 's/ERROR/错误/g' app.log  # 生成app.log.bak备份
  3. 使用正则表达式: 更灵活的匹配

    css 复制代码
    sed 's/[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/IP_HIDDEN/g'
  4. 引用匹配内容 : 使用 &

    bash 复制代码
    sed 's/ERROR/【&】/g'  # ERROR变成【ERROR】
  5. 分组引用 : 使用 \1 \2

    ini 复制代码
    sed 's/$[0-9]*$-$[0-9]*$/\2-\1/g'  # 交换两个数字

四、grep + awk + sed 组合使用

🎯 三剑客协作

在实际工作中,这三个命令经常组合使用,各司其职:

  • grep:过滤出需要的行
  • awk:提取和统计数据
  • sed:格式化和替换内容

📋 实战组合案例

案例1:分析错误日志

bash 复制代码
# 1. 提取ERROR日志,统计各类错误次数
grep "ERROR" app.log | awk '{print $5}' | sort | uniq -c | sort -rn


# 输出示例:
#   152 NullPointerException
#    89 TimeoutException
#    23 IOException

流程说明:

  1. grep "ERROR":过滤出ERROR行
  2. awk '{print $5}':提取第5列(异常类型)
  3. sort:排序
  4. uniq -c:统计重复次数
  5. sort -rn:按数量倒序

案例2:分析慢请求

perl 复制代码
# 查找响应时间>1秒的请求,格式化输出
grep "access" app.log | awk '$10 > 1000 {print $1, $7, $10}' | sed 's/ms/毫秒/g'


# 输出示例:
# 2025-12-19 /api/user 1250毫秒
# 2025-12-19 /api/order 1800毫秒

流程说明:

  1. grep "access":过滤访问日志
  2. awk '$10 > 1000':过滤响应时间>1000ms
  3. print $1, $7, $10:提取时间、URL、响应时间
  4. sed 's/ms/毫秒/g':替换单位

案例3:统计每小时错误数

bash 复制代码
# 统计每小时的ERROR数量
grep "ERROR" app.log | awk '{print substr($1,1,13)}' | sort | uniq -c


# 输出示例:
#   45 2025-12-19 10
#   89 2025-12-19 11
#   23 2025-12-19 12

流程说明:

  1. grep "ERROR":过滤ERROR行
  2. awk '{print substr($1,1,13)}':提取时间到小时(前13个字符)
  3. sort | uniq -c:统计每小时出现次数

案例4:清理和格式化异常堆栈

perl 复制代码
# 提取异常堆栈并清理格式
grep -A 20 "Exception" app.log | sed '/^$/d' | awk '{print NR, $0}'


# 输出示例:
# 1 java.lang.NullPointerException
# 2     at com.example.Service.method(Service.java:100)
# 3     at com.example.Controller.handle(Controller.java:50)

流程说明:

  1. grep -A 20 "Exception":提取异常及后20行
  2. sed '/^$/d':删除空行
  3. awk '{print NR, $0}':添加行号

案例5:分析API调用情况

bash 复制代码
# 统计API调用次数TOP10
grep "API" access.log | awk '{print $7}' | sed 's/?.*//g' | sort | uniq -c | sort -rn | head -10


# 输出示例:
#  1523 /api/user
#   892 /api/order
#   456 /api/product

流程说明:

  1. grep "API":过滤API请求
  2. awk '{print $7}':提取URL
  3. sed 's/?.*//g':去掉查询参数
  4. sort | uniq -c:统计次数
  5. sort -rn | head -10:取TOP10

案例6:生成错误报告

bash 复制代码
# 生成今日错误统计报告
{
  echo "===== 错误日志报告 ====="
  echo "日期: $(date +%Y-%m-%d)"
  echo ""
  echo "错误总数:"
  grep -c "ERROR" app.log
  echo ""
  echo "错误类型分布:"
  grep "ERROR" app.log | awk '{print $5}' | sort | uniq -c | sort -rn
  echo ""
  echo "错误时间分布:"
  grep "ERROR" app.log | awk '{print substr($1,1,13)}' | sort | uniq -c
} | tee error_report.txt

五、实战技巧总结

🎯 选择合适的工具

需求 推荐工具 原因
查找关键字 grep 最快最简单
提取特定列 awk 专门处理列数据
统计计算 awk 支持数学运算
替换文本 sed 批量替换效率高
删除行 sed 灵活的行操作
复杂分析 awk 功能最强大
组合操作 grep+awk+sed 各取所长

💡 性能优化建议

  1. 先过滤再处理: 用 grep 先缩小范围

    lua 复制代码
    grep "ERROR" huge.log | awk '{统计}'  # 好
    awk '/ERROR/ {统计}' huge.log  # 较慢
  2. 避免不必要的管道: 能一步完成不要分多步

    perl 复制代码
    awk '/ERROR/ {print $1}' log  # 好
    grep "ERROR" log | awk '{print $1}'  # 多余
  3. 使用合适的工具: 不要用 sed 做 awk 的事

    bash 复制代码
    awk '{print $1}' log  # 好
    sed 's/.* $[^ ]*$$/\1/' log  # 复杂且慢

🔍 调试技巧

  1. 逐步调试: 一步步添加管道

    perl 复制代码
    grep "ERROR" log  # 第1步:看看过滤结果
    grep "ERROR" log | awk '{print $5}'  # 第2步:看看提取结果
    grep "ERROR" log | awk '{print $5}' | sort | uniq -c  # 第3步:完整流程
  2. 使用 head 限制输出: 避免刷屏

    bash 复制代码
    grep "ERROR" huge.log | head -20  # 只看前20行
  3. 保存中间结果: 方便调试

    lua 复制代码
    grep "ERROR" log > error.tmp
    awk '{print $5}' error.tmp > types.tmp
相关推荐
陈随易2 小时前
PostgreSQL v18发布,新增AIO uuidv7 OAuth等功能
前端·后端·程序员
java1234_小锋2 小时前
[免费]基于Python的Flask+Vue物业管理系统【论文+源码+SQL脚本】
后端·python·flask·物业管理
konna2 小时前
3D模型AI生成技术分享
后端
用户298698530143 小时前
如何在 C# .NET 中将 Markdown 转换为 PDF 和 Excel:完整指南
后端·c#·markdown
golang学习记3 小时前
Jetbrains 下一代 IDE Fleet:倒下了!
后端
没带西川地图的张永年3 小时前
springboot加载配置文件几种方式
后端
镜花水月linyi3 小时前
执行SELECT/INSERT/UPDATE/DELETE的SQL语句,MySQL流程是怎么样的?
后端·mysql
狂奔小菜鸡3 小时前
Day33 | Java中的Optional
java·后端·java ee