3

I have a file with several IP addresses. There are about 900 IPs on 4 lines of txt. I would like the output to be 1 IP per line. How can I accomplish this? Based on other code, I have come up wiht this, but it fails becasue multiple IPs are on single lines:

import sys
import re

try:
    if sys.argv[1:]:
        print "File: %s" % (sys.argv[1])
        logfile = sys.argv[1]
    else:
        logfile = raw_input("Please enter a log file to parse, e.g /var/log/secure: ")
    try:
        file = open(logfile, "r")
        ips = []
        for text in file.readlines():
           text = text.rstrip()
           regex = re.findall(r'(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})$',text)
           if regex is not None and regex not in ips:
               ips.append(regex)

        for ip in ips:
           outfile = open("/tmp/list.txt", "a")
           addy = "".join(ip)
           if addy is not '':
              print "IP: %s" % (addy)
              outfile.write(addy)
              outfile.write("\n")
    finally:
        file.close()
        outfile.close()
except IOError, (errno, strerror):
        print "I/O Error(%s) : %s" % (errno, strerror)
2
  • 2
    You're looking for the canonical form of IPv4 addresses. Note that there are other acceptable forms too, even of IPv4 addresses. e.g. try 2130706433 if you run an HTTP server on localhost port 80 (2130706433 == 0x7f000001 == 127.0.0.1). Naturally, if you control the format of the file, you don't need to worry about such things... but still, if you can feasibly support IPv6 it will future-proof your script. Commented Dec 24, 2012 at 23:57
  • re.findall() always returns a list. It is never None. Commented Dec 25, 2012 at 1:09

4 Answers 4

3

The $ anchor in your expression is preventing you from finding anything but the last entry. Remove that, then use the list returned by .findall():

found = re.findall(r'(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})',text)
ips.extend(found)

re.findall() will always return a list, which could be empty.

  • if you only want unique addresses, use a set instead of a list.
  • If you need to validate IP addresses (including ignoring private-use networks and local addresses), consider using the ipaddress.IPV4Address() class.
Sign up to request clarification or add additional context in comments.

Comments

1

The findall function returns an array of matches, you aren't iterating through each match.

regex = re.findall(r'(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})$',text)
if regex is not None:
    for match in regex:
        if match not in ips:
            ips.append(match)

Comments

1

Extracting IP Addresses From File

I answered a similar question in this discussion. In short, it's a solution based on one of my ongoing projects for extracting Network and Host Based Indicators from different types of input data (e.g. string, file, blog posting, etc.): https://github.com/JohnnyWachter/intel


I would import the IPAddresses and Data classes, then use them to accomplish your task in the following manner:

#!/usr/bin/env/python

"""Extract IPv4 Addresses From Input File."""

from Data import CleanData  # Format and Clean the Input Data.
from IPAddresses import ExtractIPs  # Extract IPs From Input Data.


def get_ip_addresses(input_file_path):
    """"
    Read contents of input file and extract IPv4 Addresses.
    :param iput_file_path: fully qualified path to input file. Expecting str
    :returns: dictionary of IPv4 and IPv4-like Address lists
    :rtype: dict
    """

    input_data = []  # Empty list to house formatted input data.

    input_data.extend(CleanData(input_file_path).to_list())

    results = ExtractIPs(input_data).get_ipv4_results()

    return results
  • Now that you have a dictionary of lists, you can easily access the data you want and output it in whatever way you desire. The below example makes use of the above function; printing the results to console, and writing them to a specified output file:

    # Extract the desired data using the aforementioned function.
    ipv4_list = get_ip_addresses('/path/to/input/file')
    
    # Open your output file in 'append' mode.
    with open('/path/to/output/file', 'a') as outfile:
    
        # Ensure that the list of valid IPv4 Addresses is not empty.
        if ipv4_list['valid_ips']:
    
            for ip_address in ipv4_list['valid_ips']:
    
                # Print to console
                print(ip_address)
    
                # Write to output file.
                outfile.write(ip_address)
    

Comments

0

Without re.MULTILINE flag $ matches only at the end of string.

To make debugging easier split the code into several parts that you could test independently.

def extract_ips(data):
    return re.findall(r"\d{1,3}(?:\.\d{1,3}){3}", data)

If input file is small and you don't need to preserve original order of ips:

with open(filename) as infile, open(outfilename, "w") as outfile:
    outfile.write("\n".join(set(extract_ips(infile.read()))))

Otherwise:

with open(filename) as infile, open(outfilename, "w") as outfile:
    seen = set()
    for line in infile:
        for ip in extract_ips(line):
            if ip not in seen:
               seen.add(ip)
               print >>outfile, ip

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.