Search code examples
regexforeachtclciscocheckpoint

Using a variable between regexp inside foreach


I need to get all macs based on a list that match checkpoint and cisco vendors. I do a "show ip arp" on Cisco Router, catch the arp output and save on file. Now, I need to check line by line what macs I have.

To be exact, I need to match only the lines containing mac addresses of the Cisco and Checkpoint vendors. To do that I have to get IP address, the first forth hex digits, the dot, and more two hex digits to define a mac vendor e.g (001c.7f or e02f.6d) and print this line on a file. After that, I need to compare the "IP-ARP.txt" and "MAC-ADDRESS.txt" (this last one contains a full mac-address vendors). If my output match, save this line on another file.

Here a piece of files:

IP-ARP.txt

Internet  172.20.14.12            0   001c.7f41.186e  ARPA

Internet  172.20.14.13           57   001c.7f41.074e  ARPA

Internet  172.20.14.14            0   0200.5ebd.e17d  ARPA

Internet  172.20.19.11            -   7081.050f.9402  ARPA

Internet  172.20.19.12           54   7cad.7499.e602  ARPA

Internet  172.20.19.13            7   e02f.6d14.c1bf  ARPA

Internet  172.20.19.14          104   e02f.6d15.1d7f  ARPA

MAC-ADDRESS.txt

001c.7f

001c.ab

001b.de

001b.ff

001c.cd

001c.de

e02f.6c

e02f.7c

Thank's in advance!


Solution

  • (updated again due to changing spec)

    What we have here is a list of identifying strings (the OUI MAC address parts written as fragments of a MAC address string) and a list of data strings that need to be checked against this list.

    My solution uses the fileutil package from the Tcl library. It's not quite necessary since you could use standard Tcl commands, but it simplifies the script a lot.

    package require fileutil
    

    Define some filenames to use.

    set filename(macaddr) MAC-ADDRESS.txt
    set filename(iparp)   IP-ARP.txt
    set filename(output)  output.txt
    

    If the identifying strings list is subject to change, you may want to read it from file everytime you run your script:

    set idlist [::fileutil::cat $filename(macaddr)]
    

    Or if these addresses seldom change, you can just hard-code it in your script and edit when necessary:

    set idlist {001c.7f 001c.ab 001b.de 001b.ff 001c.cd 001c.de e02f.6c e02f.7c}
    

    Set the contents of the output file to the empty string.

    ::fileutil::writeFile $filename(output) {}
    

    To select the lines in your IP-ARP.txt file that match any of these addresses, there are several ways to traverse it. My suggestion is to use the fileutil::foreachLine command. Basic invocation is like this:

    ::fileutil::foreachLine varName filename script
    

    (The first parameter is an arbitrary variable name: on every iteration the current line will be stored in that variable. The second is the name of the file to traverse, and the third parameter is a script to run once for every line in that file.)

    The script calls a command that matches id strings using the string match command. The regexp command could be used instead, but I think that's quite unnecessary in this case. Every line in the IP-ARP.txt file is either blank or a proper Tcl list with five elements, where the MAC address is the fourth. Also, the second element is the ip number, and only those beginning with 172 are to be used. This means that the matching command can be written like this:

    proc matchid {idlist line} {
        set ipAddr  [lindex $line 1]
        set macAddr [lindex $line 3]
    
        if {[string match 172* $ipAddr]} {
            foreach id $idlist {
                if {[string match $id* $macAddr]} {
                    return "$ipAddr $macAddr\n"
                }
            }
        }
    }
    

    (Matching the ip address in this way only works if the address is in dotted decimal form: if it can be in any other form the Tcllib ip module should be used to match it.)

    The result of the command is either a line containing the ip address and the MAC address if the line matched, or the empty string if it didn't.

    Now lets traverse the contents of the IP-ARP.txt file. For each line, match the contents against the id list and get either an output string or an empty string. If the string isn't empty, append it to the output file.

    ::fileutil::foreachLine line $filename(iparp) {
        set res [matchid $idlist $line]
    
        if {$res ne {}} {
            ::fileutil::appendToFile $filename(output) $res 
        }
    }
    

    And that's it. The complete program:


    package require fileutil
    
    set filename(macaddr) MAC-ADDRESS.txt
    set filename(iparp)   IP-ARP.txt
    set filename(output)  output.txt
    
    set idlist [::fileutil::cat $filename(macaddr)]
    
    ::fileutil::writeFile $filename(output) {}
    
    proc matchid {idlist line} {
        set ipAddr  [lindex $line 1]
        set macAddr [lindex $line 3]
    
        if {[string match 172* $ipAddr]} {
            foreach id $idlist {
                if {[string match $id* $macAddr]} {
                    return "$ipAddr $macAddr\n"
                }
            }
        }
    }
    
    ::fileutil::foreachLine line $filename(iparp) {
        set res [matchid $idlist $line]
    
        if {$res ne {}} {
            ::fileutil::appendToFile $filename(output) $res 
        }
    }
    

    Documentation for the Tcllib fileutil module

    Documentation: foreach, if, lindex, package, proc, set, string

    (Note: the 'Hoodiecrow' mentioned in the comments is me, I used that nick earlier.)