Python re.match only matches before the first \n

Viewed 128

I'm trying to wrap ping with Python (3.7.4) using subprocess and re.

The stdout from the subprocess function is bytes array so I had to change the regex type to match the case.

    import subprocess,re

    out = subprocess.run(['ping', '-c', '1', '8.8.8.8'], capture_output=True)
    print(out.stdout)
    match = re.match(br'P(..)G', out.stdout, re.DOTALL | re.MULTILINE)
    if match:
        print(match.groups())

    match = re.match(br'trans(.)', out.stdout, re.DOTALL | re.MULTILINE)
    if match:
        print(match.groups())

The actual output from the ping command:

b'PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.\n64 bytes from 8.8.8.8: icmp_seq=1 ttl=53 time=60.7 ms\n\n--- 8.8.8.8 ping statistics ---\n1 packets transmitted, 1 received, 0% packet loss, time 0ms\nrtt min/avg/max/mdev = 60.665/60.665/60.665/0.000 ms\n'

The first output of match.groups:

(b'IN',)

The second one is empty (should be (b'm',)), in fact, everything after the first \n cannot be matched.

Notice I have re.MULTILINE, converting to str using str() or .decode() didn't have any effect on the output.

Checked with several different online tools, they all worked, any ideas?

1 Answers

When you use match the matching start from the first position, your variable doesn't start with trans and this is why it didn't mach it, use .*?trans(.) to indicate that trans is in middle of the text, but I think you should use search:

   match = re.search(br'trans(.)', out.stdout)

Note:

  • re.DOTALL is only used when you want to include \n in ., this mean . will match any character including \n.
  • re.MULTILINE by default ^ match the begin of the text, and $ the end of the text, but when you compile your REGEX with this flag, ^ will match the begin of line and $ the end of line (\n).

The problem in your case was the way match work check this example:

import re

pattern = r'HELLO (\w+)'

print(re.match(pattern, 'HELLO X').groups())  # work fine because the text start with HELLO 
m = re.match(pattern, 'CHELLO X')
print(m is None)  # didn't mach because the Text didn't start with HELLO

match start the matching from the first position when you don't specify that the Hello is preceded by some characters.

To explain DOTALL:

import re

text = '\nHELLO X'
pattern = re.compile(r'.*?HELLO (\w+)')
pattern_dotall = re.compile(r'.*?HELLO (\w+)', re.DOTALL)

print(re.match(pattern, text) is None)  # True: . don't match \n
print(re.match(pattern_dotall, text) is None)  # False: here is included
Related