PHP parse Ping command output with preg_match

Viewed 76

I would like to parse the output of the ping command in Linux. My current code is working fine, the only problem is, that it only parse the first result. I would like to change it to parse every line.

The red marked lines should also be parsed: https://i.imgur.com/GDE19Xl.png What do I have to change to do that?

That's my current regular expression: https://regex101.com/r/uH0XwP/1

That's the current PHP code:

$re = '/^PING\b[^(]*\(([^)]*)\)\s([^.]*)\..*?^(\d+\sbytes).*?icmp_seq=(\d+).*?ttl=(\d+).*?time=(.*?ms).*?(\d+)\spackets\stransmitted.*?(\d+)\sreceived.*?(\d+%)\spacket\sloss.*?time\s(\d+ms).*?=\s([^\/]*)\/([^\/]*)\/([^\/]*)\/(.*?)\sms/ims';
$str = 'PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=58 time=0.816 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=58 time=0.883 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=58 time=0.743 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=58 time=0.613 ms

--- 1.1.1.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3033ms
rtt min/avg/max/mdev = 0.613/0.763/0.883/0.105 ms
';

preg_match($re, $str, $matches, PREG_OFFSET_CAPTURE, 0);

// Print the entire match result
var_dump($matches);

Thank a lot. With best regards

1 Answers

If your data is structured like that, you might make use of the \G anchor

(?:^PING\b[^(]*\(([^)]*)\)\s([^.]*)\..*|\G(?!^))\R^(\d+\sbytes).*?icmp_seq=(\d+).*?ttl=(\d+).*?time=(.*?ms).*(?:\s+---.*\R(\d+)\spackets\stransmitted.*?(\d+)\sreceived.*?(\d+%)\spacket\sloss.*?time\s(\d+ms)\R.*=\s([^\/]*)\/([^\/]*)\/([^\/]*)\/(.*?)\sms)?

Regex demo

In your pattern there are multiple occurrences of .*? that might also be replaced by a more specific part like spaces or digits with an optional decimal part

(?:^PING\b[^(]*\(([^)]*)\)\s([^.]*)\.|\G(?!^))\R(\d+\sbytes).*?icmp_seq=(\d+)\h+ttl=(\d+).*?time=(\d+(?:\.\d+)?\h+ms).*(?:\s+---.*\R(\d+)\spackets\stransmitted.*?(\d+)\sreceived,\h*(\d+%)\spacket\sloss,\h+time\s(\d+ms)\R.*=\s(\d+(?:\.\d+)?)\/(\d+(?:\.\d+)?)\/(\d+(?:\.\d+)?)\/(\d+(?:\.\d+)?)\sms)?

Then using preg_match_all the values are in group 3 - group 6

PHP demo

Another option is to match the repeating data in between in a capture group, and process those lines separately. In the next pattern, capture group 3 contains the repeating lines that you could parse separately in a second step.

^PING\b[^(]*\(([^)]*)\)\s([^.]*)\.\R(\d+\sbytes.*?icmp_seq=\d+\h+ttl=\d+\h+time=\d+(?:\.\d+)?\h+ms(?:\R\d+\sbytes.*?icmp_seq=\d+\h+ttl=\d+\h+time=\d+(?:\.\d+)?\h+ms)*)\R+---.*\R(\d+)\spackets\stransmitted.*?(\d+)\sreceived,\h*(\d+%)\spacket\sloss,\h+time\s(\d+ms)\R.*=\s(\d+(?:\.\d+)?)\/(\d+(?:\.\d+)?)\/(\d+(?:\.\d+)?)\/(\d+(?:\.\d+)?)\sms

See another regex demo.

Related