前言

ping 命令是一种比较好用的网络诊断工具,常用来验证链路问题,例如 ping traceroute mtr 都使用的 “ICMP” 包来测试 Internet 两点之间的网络连接状况。生产环境中, 网络是否稳定 (网络时延) 是一个很重要的指标. 为了方便检查网络时延的大小, 我们可以通过 ping 命令实现长时间的网络监控。

本文主要记录了 Linux 环境如何使用 ping 命令 + 时间戳实时输出保存到文件里面的解决方案

更新历史

2021 年 06 月 06 日 - 初稿

阅读原文 - https://wsgzao.github.io/post/ping/


ping 简介

ping 大家可能每天都在使用,不多做介绍了

ping(呯)是一种计算机网络工具,用来测试数据包能否透过 IP 协议到达特定主机。ping 的运作原理是向目标主机传出一个 ICMP 的请求回显数据包,并等待接收回显回应数据包。程序会按时间和成功响应的次数估算丢失数据包率(丢包率)和数据包往返时间(网络时延,Round-trip delay time)。

直接 ping ip 即可。

若显示 ping 的回显时间,此命令也提供了参数 -D 来回显时间戳。

1
2
3
4
5
6
7
8
9
10
11
12
13
# ping baidu.com -D
PING baidu.com (39.156.69.79) 56(84) bytes of data.
[1623205720.047547] 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=1 ttl=23 time=274 ms
[1623205720.321747] 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=2 ttl=23 time=274 ms
[1623205721.322361] 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=3 ttl=23 time=274 ms
[1623205722.323220] 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=4 ttl=23 time=274 ms
[1623205723.324359] 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=5 ttl=23 time=274 ms

# 文章后面介绍了两种方法,我倾向于使用 fflush()
ping baidu.com -i 1 | awk '{ print strftime("%Y-%m-%d-%H:%M:%S",systime())" | "$0; fflush() }' >> ping-baidu.com.txt &
2021-06-11-07:24:58 | 64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=1 ttl=37 time=182 ms
2021-06-11-07:24:59 | 64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=2 ttl=37 time=181 ms
2021-06-11-07:25:01 | 64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=4 ttl=37 time=181 ms

然而,时间戳可读性较差,虽然可以利用网上的一些工具(unitxtime)来转化,但是比较麻烦,最好的方式时回显时就是可读性较好的时间格式。

ping 命令的使用

常用参数

-i: 每次执行 ping 操作的间隔时间, 默认是 1s;

-c: 执行 ping 操作的次数, 默认是一直执行, 除非被中断;

-s: 指定执行 ping 操作时发送的包的大小, 默认是 56B, 添加报文头之后, 最终发送的是 64B.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 在终端 ping 某个地址, 执行 10 次
ping baidu.com -c 10 | awk '{ print $0"\t"strftime("%H:%M:%S",systime()) }'
64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=1 ttl=40 time=83.3 ms 10:41:23
64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=2 ttl=40 time=83.4 ms 10:41:24
64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=3 ttl=40 time=83.4 ms 10:41:25

# 日期在后面
ping baidu.com | awk '{ print $0"\t"strftime("%Y-%m-%d %H:%M:%S",systime()); fflush()}'
64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=1 ttl=40 time=83.2 ms 2021-06-09 10:42:45
64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=2 ttl=40 time=83.3 ms 2021-06-09 10:42:46
64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=3 ttl=40 time=83.3 ms 2021-06-09 10:42:47

# 日前在前面
ping baidu.com | awk '{ print strftime("%Y.%m.%d %H:%M:%S",systime())"\t"$0; fflush() }'
2021.06.09 10:43:28 64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=1 ttl=46 time=162 ms
2021.06.09 10:43:29 64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=2 ttl=46 time=177 ms
2021.06.09 10:43:30 64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=3 ttl=46 time=174 ms

ping 将输出重定向到指定文件

使用 fflush

注意:使用 fflush(),不然文件不会有信息,因为 awk 也是有缓存的。

为防止脚本被中断, 可以通过 nohup 令脚本在后台执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 下面未加 fflush(),执行命令生成文件会等一会才会有信息打印到文件里
nohup ping baidu.com | awk '{ print strftime("%Y-%m-%d %H:%M:%S",systime())"\t" $0; fflush() }' >> long_ping.txt &
$ tail -f long_ping.txt
2021-06-09 10:45:54 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=2 ttl=40 time=83.3 ms
2021-06-09 10:45:55 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=3 ttl=40 time=83.3 ms
2021-06-09 10:45:56 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=4 ttl=40 time=83.3 ms

# 要结束后台进程, 可通过下述方式查找并 kill
$ ps -ef |grep ping
user00 5778 30382 0 10:45 pts/2 00:00:00 ping baidu.com
user00 7133 30382 0 10:48 pts/2 00:00:00 grep --color=auto ping
$ kill -9 5778
[1]+ Done nohup ping baidu.com | awk '{ print strftime("%Y.%m.%d %H:%M:%S",systime())"\t" $0; fflush() }' >> long_ping.txt

使用 pong

  1. 什么是 pingpong?

pingpong 是一种数据缓存的手段,通过 pingpong 操作可以提高数据传输的效率。

  1. 什么时候需要 pingpong?

在两个模块间交换数据时,上一级处理的结果不能马上被下一级所处理完成,这样上一级必须等待下一级处理完成才可以送新的数据,这样就会对性能产生很大的损失。

引入 pingpong 后我们可以不去等待下一级处理结束,而是将结果保存在 pong 路的缓存中,pong 路的数据准备好的时刻,ping 路的数据也处理完毕(下一级),然后无需等待直接处理 pong 路数据,上一级也无需等待,转而将结果存储在 ping 路。这样便提高了处理效率。

1
nohup ping baidu.com -i 1 | while read pong; do echo "$(date +"%Y-%m-%d %H:%M:%S") | $pong"; done | tee -a ping-baidu.com.log &

date 时间戳

数据戳转化

date 可以将时间戳转化为 localtime。

1
2
3
4
5
# date -d @1623205723.324359
Wed Jun 9 10:28:43 CST 2021
# date --date=@1623205723.324359
Wed Jun 9 10:28:43 CST 2021
利用 awk 进行转化,比较麻烦。

awk 拼接

1
2
3
4
5
6
7
8
9
10
11
# 格式可以自定义调整
ping baidu.com | awk '{"date" | getline date; print date,$0}'
Wed Jun 9 10:33:01 CST 2021 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=2 ttl=40 time=83.3 ms
Wed Jun 9 10:33:01 CST 2021 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=3 ttl=40 time=83.5 ms
Wed Jun 9 10:33:01 CST 2021 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=4 ttl=40 time=83.3 ms

# 时间格式可根据 date 自定义
ping baidu.com | awk -v date="$(date +"%Y-%m-%d %r")" '{print date, $0}'
2021-06-09 10:33:38 AM 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=1 ttl=40 time=83.3 ms
2021-06-09 10:33:38 AM 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=2 ttl=40 time=83.5 ms
2021-06-09 10:33:38 AM 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=3 ttl=40 time=83.6 ms

perl

如果 awk 没有 strftime()

Notice:报错“Can’t locate Time/Piece.pm in @INC”,需要执行命令 yum -y install perl-Time-Piece 来进行必要包的安装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 要将其重定向到文件,请使用标准 shell 重定向并关闭输出缓冲:
ping baidu.com | perl -nle 'print scalar(localtime), " ", $_'
Wed Jun 9 10:36:14 2021 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=2 ttl=40 time=83.4 ms
Wed Jun 9 10:36:15 2021 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=3 ttl=40 time=83.5 ms
Wed Jun 9 10:36:16 2021 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=4 ttl=40 time=83.3 ms

# 如果显示 ISO8601 时间格式
ping baidu.com | perl -nle 'BEGIN {$|++} print scalar(localtime), " ", $_'
Wed Jun 9 10:36:41 2021 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=1 ttl=40 time=83.3 ms
Wed Jun 9 10:36:42 2021 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=2 ttl=40 time=83.3 ms
Wed Jun 9 10:36:43 2021 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=3 ttl=40 time=83.5 ms

ping baidu.com | perl -nle 'use Time::Piece; BEGIN {$|++} print localtime->datetime, " ", $_'
2021-06-09T10:37:08 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=1 ttl=40 time=83.4 ms
2021-06-09T10:37:09 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=2 ttl=40 time=83.4 ms
2021-06-09T10:37:10 64 bytes from 39.156.69.79 (39.156.69.79): icmp_seq=3 ttl=40 time=83.2 ms

知识补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
strftime 补充:
函数 strftime()的操作有些类似于 sprintf():识别以百分号 (%) 开始的格式命令集合,格式化输出结果放在一个字符串中。格式化命令说明串 strDest 中各种日期和时间信息的确切表示方法。格式串中的其他字符原样放进串中。格式命令列在下面,它们是区分大小写的。
%a 星期几的简写
%A 星期几的全称
%b 月份的简写
%B 月份的全称
%c 标准的日期的时间串
%C 年份的前两位数字
%d 十进制表示的每月的第几天
%D 月 / 天 / 年
%e 在两字符域中,十进制表示的每月的第几天
%F 年 - 月 - 日
%g 年份的后两位数字,使用基于周的年
%G 年份,使用基于周的年
%h 简写的月份名
%H 24 小时制的小时
%I 12 小时制的小时
%j 十进制表示的每年的第几天
%m 十进制表示的月份
%M 十时制表示的分钟数
%n 新行符
%p 本地的 AM 或 PM 的等价显示
%r 12 小时的时间
%R 显示小时和分钟:hh:mm
%S 十进制的秒数
%t 水平制表符
%T 显示时分秒:hh:mm:ss
%u 每周的第几天,星期一为第一天 (值从 1 到 7,星期一为 1)
%U 第年的第几周,把星期日作为第一天(值从 0 到 53)
%V 每年的第几周,使用基于周的年
%w 十进制表示的星期几(值从 0 到 6,星期天为 0)
%W 每年的第几周,把星期一做为第一天(值从 0 到 53)
%x 标准的日期串
%X 标准的时间串
%y 不带世纪的十进制年份(值从 0 到 99)
%Y 带世纪部分的十制年份
%z,%Z 时区名称,如果不能得到时区名称则返回空字符。
%% 百分号
语法
strftime(format,timestamp) 参数 描述
format 可选。规定如何返回结果。
timestamp 可选。时间戳,默认是当前本地的


awk 补充:

awk 工作流程是这样的:先执行 BEGING,然后读取文件,读入有 / n 换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0 则表示所有域,$1 表示第一个域,$n 表示第 n 个域, 随后开始执行模式所对应的动作 action。接着开始读入第二条记录 ······ 直到所有的记录都读完,最后执行 END 操作。


print 与 printf 补充:

print 中不能使用 %s ,%d 或 %c;print 自动换行,printf 没有自动换行
文章目录
  1. 1. 前言
  2. 2. 更新历史
  3. 3. ping 简介
  4. 4. ping 命令的使用
  5. 5. ping 将输出重定向到指定文件
    1. 5.1. 使用 fflush
    2. 5.2. 使用 pong
  6. 6. date 时间戳
    1. 6.1. 数据戳转化
    2. 6.2. awk 拼接
    3. 6.3. perl
  7. 7. 知识补充