在网络排查里最让人头疼的,往往不是不知道问题在哪,而是数据太多不知道从哪看起。有一次我负责一个线上服务,流量高峰时出现卡顿,抓了两小时的包,文件差不多 8GB。用 Wireshark 打开之后,鼠标滚轮都要滚出火星子,卡得基本没法操作。后来我把包丢给 tshark,让它按照字段输出,再用 awk 做聚合,几分钟就定位到几个异常 IP。那之后我就养成了习惯:凡是要做流量分析,优先用命令行。

tshark 是 Wireshark 的命令行版本,它不靠鼠标,而是靠参数和数据流工作。awk 是文本处理中的统计能手,能够对每一行做条件判断、累加、排序前的数据处理。sed 则擅长做替换和删除,适合清洗日志。三者配合能形成一条高效的流水线:tshark 负责把二进制包变成文本,awk 负责把文本变成数字结论,sed 负责把结论变成好看的报告。

需要注意,本文所有示例都基于同一个技术栈:Shell 命令行环境,配合 tshark、awk、sed 三个命令。不涉及 Wireshark 图形界面,也不引入 Python 等其他语言。

一、应用场景:什么时候需要这套组合?

日常开发中,流量分析不一定只在出问题时才做。有些团队每周都要统计外网访问量,有些安全同事需要每天检查有没有扫描行为,还有一些运维任务需要定期分析慢连接。这些事情如果靠人工打开抓包文件一屏一屏地看,既耗时又容易漏。

1.1 批量分析网络流量日志

所谓批量分析,就是一次性处理大量网络包,而不是单包去看。比如想知道某个时段内,哪些 IP 访问次数最多,哪些 IP 消耗流量最大,或者哪个端口最活跃。这类问题适合用 tshark 先把所有包转成文本,然后用 awk 做分组统计。

1.2 自动化报告生成

报告不是给人一眼一眼盯出来的,而是可以定时跑出来的。服务器上放一个脚本,每天凌晨读取昨天的 pcap 文件,自动生成一份 Markdown 报告,再发给相关同事。整个过程不需要人工介入,这就是自动化的价值。

1.3 异常告警

当流量行为偏离正常范围,比如某个 IP 突然发了大量请求,或者某个端口出现异常重传,脚本能够在分析完成后立刻发出告警。这种告警比人工发现快很多,而且可以在深夜自动运行。

二、技术优缺点分析

了解工具的优缺点,能帮你决定在什么场景下使用它们。

2.1 优点

第一,没有图形界面依赖,适合在服务器上运行。服务器往往没有桌面环境,图形化工具装起来不方便,而 tshark 是纯命令行,ssh 进去就能操作。

第二,处理能力稳定。tshark 作为 Wireshark 家族成员,对常见协议的解析非常成熟,输出文本的速度也很快,几 GB 的 pcap 文件处理起来虽然要等一会儿,但不会像图形界面那样卡到无响应。

第三,可组合性强。tshark 的输出可以直接用管道交给 awk、sed、sort、grep 等命令,这意味着你可以按照自己的需要设计统计逻辑,而不是受制于某个固定面板。

第四,学习曲线平缓。如果你已经熟悉 Linux 命令行,掌握 tshark 的常用输出参数很快;awk 和 sed 的基础用法也只需要几小时。

2.2 缺点

第一,初期调试字段名成本高。tshark 的字段名非常细,比如 ip.src、tcp.dstport、http.request.uri,记不住的时候需要反复查,不如图形界面点选方便。

第二,输出格式必须自己控制。tshark 默认输出是一行一个包,但字段间用什么分隔、空值怎么处理,都需要通过参数指定,否则后续 awk 解析容易出错。

第三,复杂协议的深度分析不够直观。Wireshark 图形界面可以展示完整的协议层次,而命令行输出需要自己构造字段组合,遇到 HTTP/2、gRPC 这类复杂协议时,配置参数会变得繁琐。

第四,缺少交互式体验。查一个包时用 Wireshark 更方便,批量处理时命令行更好。两者适合不同场景,不必互相替代。

三、准备工作:让 tshark 输出干净数据

在用 awk 之前,先要让 tshark 输出能直接喂给 awk 的字段。tshark 的 -T 参数负责设置输出格式,-e 参数指定要输出的字段。下面这段命令,读取一个抓包文件,并输出序号、时间、源 IP、目标 IP、协议号、包长度。

# 技术栈:Shell + tshark + awk + sed
# 从 flow.pcap 中提取最常用的几个字段,输出为 CSV 格式

tshark -r flow.pcap \
  -T fields \                  # 使用纯字段输出模式
  -e frame.number \            # 包序号
  -e frame.time \              # 包时间
  -e ip.src \                  # 源 IP
  -e ip.dst \                  # 目标 IP
  -e ip.proto \                # 协议号,例如 6 代表 TCP
  -e frame.len \               # 帧长度,字节数
  -E header=y \                # 第一行输出字段名称
  -E separator=,               # 使用逗号分隔
  > flow.csv                   # 保存到文件

运行这段命令后,会得到一个 flow.csv。头部是字段名称,之后每一行对应一个包。用 head 和 wc 可以快速查看概貌,但注意不要在正式报告里用这两个命令,它们适合排查问题。

这里有个细节:字段输出时如果用逗号分隔,那么 IP 地址和时间字符串里也包含逗号? 不会。时间格式可能包含空格,所以如果想让 awk 按空格切分,建议用制表符分隔。下面改用制表符:

# 技术栈:Shell + tshark + awk + sed
# 使用制表符分隔,方便 awk 按空格切分

tshark -r flow.pcap \
  -T fields \
  -e frame.number \
  -e frame.time \
  -e ip.src \
  -e ip.dst \
  -e frame.len \
  -E separator='\t' \
  > flow.tsv

这里用制表符分隔,awk 默认按空格和制表符切分字段,所以后续处理更省事。

四、用 awk 做流量统计

awk 适合做分组求和的活。下面统计每个源 IP 的包数和总字节数,按总字节数从大到小排序,取前 20 个。

# 技术栈:Shell + tshark + awk + sed
# 统计每个源 IP 的包数和总字节数

tshark -r flow.pcap \
  -T fields \
  -e ip.src \
  -e ip.dst \
  -e frame.len | \

awk '
{
  # $1 是源 IP,$3 是帧长度
  bytes[$1] += $3;   # 累加字节数
  count[$1] += 1;    # 累加包数
}
END {
  # 遍历所有源 IP
  for (ip in bytes) {
    print ip, count[ip], bytes[ip];
  }
}' | sort -k3 -nr | head -20

这段 awk 代码虽然短,但逻辑完整。数组 bytes 和 count 分别以 IP 作为下标,最后在 END 阶段输出。之后用 sort 按第三列数字降序,再用 head 截取前 20 行。

4.1 按目标端口统计

继续看看访问最多的目标端口。这里用 tcp.dstport 字段,表示 TCP 目标端口。为了只看连接建立包,可以加一个过滤条件 tcp.flags.syn==1,这样统计的是新连接请求。

# 技术栈:Shell + tshark + awk + sed
# 统计 TCP 新建连接的目标端口分布

tshark -r flow.pcap \
  -Y "tcp.flags.syn==1" \      # 只保留 SYN 包
  -T fields \
  -e tcp.dstport | \

awk '
{
  port_count[$1]++;   # 端口计数加一
}
END {
  for (port in port_count) {
    print port, port_count[port];
  }
}' | sort -k2 -nr | head -10

如果看到 22、3306、6379 这类端口频繁出现,但业务上不应该对外开放,那大概率是扫描行为。

4.2 找出慢连接

在 TCP 分析中,慢通常表现为连接持续时间长,或者重传频繁。先看连接持续时间:用 tcp.stream 字段标识一条连接,用 frame.time_relative 表示包相对时间。同一个连接第一次出现的时间就是开始时间,最后一次出现的时间就是结束时间。

# 技术栈:Shell + tshark + awk + sed
# 根据 tcp.stream 计算每条连接的持续时间

tshark -r flow.pcap \
  -T fields \
  -e tcp.stream \
  -e frame.time_relative | \

awk '
{
  # 如果这个流还没有记录开始时间,就记下来
  if (!($1 in first_time)) {
    first_time[$1] = $2;
  }
  # 每次出现都更新最后时间
  last_time[$1] = $2;
}
END {
  for (s in first_time) {
    duration = last_time[s] - first_time[s];
    if (duration > 5) {   # 超过 5 秒的慢连接
      print s, duration;
    }
  }
}' | sort -k2 -nr | head -5

这个统计能帮忙找出超过 5 秒的 TCP 连接。如果公司业务普遍要求秒级响应,这些慢连接就是需要重点排查的对象。

五、用 sed 做文本清洗

awk 负责统计,sed 负责改写。tshark 输出的字段可能会出现空值,例如非 TCP 包没有 tcp.dstport 字段,输出时就会有两个分隔符连在一起。sed 可以把空值替换成 0 或者 null。

# 技术栈:Shell + tshark + awk + sed
# 把逗号分隔文件中的空值替换成 0
# 假设文件里形如:123,2024-01-01,10.0.0.1,,456

sed 's/,,/,0,/g; s/,$/,0/' data.csv > data_clean.csv

这条命令处理两种空值:一种是处于中间位置的空字段,另一种是行尾的空字段。注意这里用分号把两个替换表达式放在一起。

sed 也可以用来调整时间格式。比如把 tshark 默认的月份英文缩写改成两位数字,方便生成报表。

# 技术栈:Shell + tshark + awk + sed
# 将月份缩写替换成数字

echo "Jan 15 10:00:00 2025" | \
sed -E 's/Jan/01/g; s/Feb/02/g; s/Mar/03/g; s/Apr/04/g; s/May/05/g;'

虽然这个示例用 echo 模拟了一行数据,但实际场景中,你可以把 tshark 输出的时间字段先取出来,再通过 sed 做标准化。记住,sed 适合处理有规律的简单文本,如果涉及复杂的条件逻辑,还是交给 awk 更清晰。

六、自动生成报告

有了前面的统计结果,就可以把这些结果拼装成 Markdown 报告。下面是一个完整的脚本,它会读取一个 pcap 文件,统计源 IP 的流量,然后生成带表格的报告。

#!/bin/bash
# 技术栈:Shell + tshark + awk + sed
# 功能:批量分析 pcap 文件并生成 Markdown 报告
# 用法:./build_report.sh flow.pcap

PCAP="$1"                             # 输入文件
REPORT="report.md"                    # 输出文件
TEMP_FILE="/tmp/flow_stat.txt"        # 中间文件

# 第一步:用 tshark 提取源 IP 和包长度
tshark -r "$PCAP" \
  -T fields \
  -e ip.src \
  -e frame.len > "$TEMP_FILE"

# 第二步:用 awk 统计每个源 IP 的包数和字节数
awk '
{
  bytes[$1] += $2;   # 累加字节
  count[$1] += 1;    # 累加包数
}
END {
  for (ip in bytes) {
    printf "%s %d %d\n", ip, count[ip], bytes[ip];
  }
}' "$TEMP_FILE" | sort -k3 -nr > /tmp/ip_rank.txt

# 第三步:生成 Markdown 报告头部
cat > "$REPORT" <<'EOF'
# 流量分析报告

## 源 IP 流量排行

| 源 IP | 包数 | 总字节 |
|-------|------|--------|
EOF

# 第四步:把统计结果转成表格行,追加到报告
awk '{printf "| %s | %d | %d |\n", $1, $2, $3}' /tmp/ip_rank.txt >> "$REPORT"

# 第五步:用 sed 在报告头部插入生成时间
sed -i "s/# 流量分析报告/# 流量分析报告\n\n生成时间:$(date '+%Y-%m-%d %H:%M:%S')/" "$REPORT"

echo "报告已生成:$REPORT"

这个脚本完全可以放进 cron 里。比如每天凌晨三点跑一次,分析昨天的抓包文件:

# 技术栈:Shell + tshark + awk + sed
# 每天 3 点生成一次报告
0 3 * * * /home/user/bin/build_report.sh /data/pcap/$(date -d yesterday +\%Y\%m\%d).pcap

注意 cron 行中百分号需要转义。如果服务器时间不是中国时区,还需要配置 cron 的时区。

七、异常告警

报告是事后总结,告警是事前提醒。用 awk 判断每个源 IP 的流量是否超过阈值,一旦超过就写进日志或发给 webhook。

#!/bin/bash
# 技术栈:Shell + tshark + awk + sed
# 功能:检查流量异常并触发告警
# 用法:./alert.sh flow.pcap 104857600

PCAP="$1"
THRESHOLD="${2:-104857600}"    # 默认 100MB

# 用 tshark 和 awk 找出超过阈值的源 IP
tshark -r "$PCAP" -T fields -e ip.src -e frame.len | \
awk '
BEGIN {
  # 把外部传入的阈值放进变量
  limit = threshold;
}
{
  bytes[$1] += $2;
}
END {
  for (ip in bytes) {
    if (bytes[ip] > limit) {
      printf "%s %d\n", ip, bytes[ip];
    }
  }
}' threshold="$THRESHOLD" > /tmp/exceed_ip.txt

# 如果文件非空,说明存在异常
if [ -s /tmp/exceed_ip.txt ]; then
  while read ip bytes; do
    # 写入系统日志,方便集中收集
    logger -t net_alert "异常流量: $ip 产生 $bytes 字节"

    # 如果配置了 webhook,可以取消下面这行的注释
    # curl -s -X POST "https://hooks.example.com/alert" \
    #      -H "Content-Type: application/json" \
    #      -d "{\"ip\":\"$ip\",\"bytes\":$bytes}"
  done < /tmp/exceed_ip.txt
else
  echo "流量正常"
fi

这里用 threshold="$THRESHOLD" 把外部参数传给 awk,是一种常见做法。你也可以把这段脚本加入 crontab,每半小时检查一次。注意 webhook 地址不要写死在脚本里,最好用环境变量。

八、注意事项

第一,字段名必须确认准确。同一个字段在不同 tshark 版本中可能会有差异,写脚本前最好先查询字段列表,确认无误后再开始。

第二,注意空字段。不同协议的数据包,字段情况差别很大。比如非 TCP 包没有 tcp.dstport,输出时会出现连续分隔符。awk 处理时可能把空字段当成 0,也可能当成空字符串,需要针对性地处理。

第三,大文件处理要耐心。tshark 读取几个 GB 的 pcap 需要时间,awk 统计也需要时间。建议先用小文件验证脚本,再跑大规模数据,避免浪费时间。

第四,awk 数组默认按哈希存储,输出顺序不固定,所以需要 sort 来排序。不要在 END 里假设数组顺序。

第五,sed 替换特殊字符时要注意转义。比如替换 IP 地址中的点号,点号在正则中代表任意字符,需要写成点号前面加反斜杠。

第六,在 cron 中运行脚本时,尽量使用绝对路径,并且设置 PATH 变量。因为 cron 环境很干净,tshark、awk、sed 的位置可能不在默认路径中。

第七,注意磁盘空间。tshark 转出的文本文件可能比 pcap 还大,中间文件要及时清理,或者使用管道避免大量中间文件。

九、文章总结

tshark 和 awk、sed 的组合,是命令行党做网络流量分析的利器。tshark 负责把二进制包解析成文本,awk 负责统计和判断,sed 负责清洗和美化。三者都是 Unix 哲学里的老牌工具,配合起来灵活度极高。无论是生成每日报告,还是对流量异常做实时告警,这套组合都能胜任。你不需要记住每个字段,只需要掌握一套核心流程,再遇到具体问题时就查字段表,用同样的套路解决。希望这篇文章能帮你打开命令行流量分析的门,下次遇到 pcap 文件,试试用 tshark 加 awk 加 sed 来解决。