#!/usr/bin/perl -w use strict; ##################################################################################### # This script contains two handy functions to assist in the parsing of # program output. # The nice thing about it is that it is fairly field position agnostic so it won't # be so fragile. As long as the names are present and don't change it will work. # Even if they are different widths or in different positions. # # There are two functions. # findpositions(): # pass it an array of needed headers and the header line. # returns an array ref of results. els are ['headername', 'stpos', 'endpos'] # # getfields() # pass it the ref returned from findpositions and the scalar containing data. # returns a hashref of name=>value for each position. # # Caveats: # These snippets expect that columns NEVER overlap and they are left justified. # Trailing whitespace in values is removed. ##################################################################################### sub findpositions(\@$){ my $headar = shift; my $line = shift; my @results; my @errors = (); my $pos = 1; for (my $i = 0; $i < @$headar; $i++){ my $header = quotemeta($headar->[$i]); if ($line=~/$header\s*/g){ push @results, [ $headar->[$i], $pos - 1, pos($line) - ($pos - 1) ]; $pos = pos($line); }else { # Failure push @errors, "Couldn't find $headar->[$i]\n" if $^W; } } warn @errors if $^W; return if @errors; return \@results; } sub getfields{ my $aref = shift; # Headers my $line = shift; # Data my %res = (); foreach (@$aref){ $res{$_->[0]} = substr($line,$_->[1],$_->[2]); $res{$_->[0]} =~s/\s+$//; } return \%res; } ########################################################### # Sample # Run netstat ########################################################### # Headers we are expecting. my @ACTIVE_INET_HEADERS = ( 'Proto', 'Recv-Q', 'Send-Q', 'Local Address', 'Foreign Address', 'State', 'User', 'Inode', 'PID/Program name', ); my @ACTIVE_UNIX_HEADERS = ( 'Proto', 'RefCnt', 'Flags', 'Type', 'State', 'I-Node', 'PID/Program name', 'Path', ); ################## # Run command ################## my @output = `netstat -apve`; die "Couldn't run netstat" unless @output; my $href; # Headers; while (my $line = shift @output){ chomp $line; ########################################################### # Parse Internet sockets. ########################################################### if (!$href && $line=~/^Active Internet connections/ ){ print "Active Internet connections.\n","#" x 30,"\n"; $line = shift @output; if ( $line=~/^Proto/ ){ $href = findpositions(@ACTIVE_INET_HEADERS,$line); die "Couldn't parse headers!" unless $href; next; }else{ die "Unexpected: $line"; } } ########################################################### # Parse Unix sockets. ########################################################### if ($line=~/^Active UNIX domain sockets/ ){ print "\nActive UNIX sockets.\n","#" x 30,"\n"; $line = shift @output; # Parse Internet sockets. if ( $line=~/^Proto/ ){ $href = findpositions(@ACTIVE_UNIX_HEADERS,$line); die "Couldn't parse headers!" unless $href; next; }else{ die "Unexpected: $line"; } } if ($href){ my $lhref = getfields($href,$line); # Hash with name=>value ################################################################################# # Why doesn't the line below get flattened? It only prints the first list. # print ( map{"\t$_ =\> $lhref->{$_}\n"} sort keys %$lhref) , "#" x 40, "\n" ; # Why bad? # Anybody? Bueller... Bueller? I suppose two print statements wouldn't kill me. ################################################################################# print map{"\t$_ =\> $lhref->{$_}\n"} sort keys %$lhref; print "#" x 40, "\n"; } } print "Done!\n"; __END__ >netstat -apve sample data Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State User Inode PID/Program name tcp 0 0 somedomainher.net:www trder102.stare.ya:46312 TIME_WAIT user 0 - tcp 0 0 somedomainher.ne:tproxy trder104.stare.ya:25013 TIME_WAIT user 0 - tcp 0 0 somedomainher.net:www trder104.stare.ya:49319 TIME_WAIT user 0 - tcp 0 0 somedomainher.net:www trder104.stare.ya:28205 TIME_WAIT user 0 - tcp 0 0 somedomainher.net:www trder104.stare.ya:28202 TIME_WAIT user 0 - tcp 0 0 *:www *:* LISTEN user 1957270 16874/httpd tcp 0 424 somedomainher.net:ssh 209.X.XXX.XXX:1115 ESTABLISHED user 1582988 947/sshd2 tcp 0 0 somedomains:netbios-ssn 209.6.XXX.XXX:1026 ESTABLISHED user 1582180 892/smbd tcp 0 0 *:https *:* LISTEN user 714 607/httpd tcp 0 0 *:mysql *:* LISTEN user 654 594/mysqld tcp 0 0 *:ssh *:* LISTEN user 576 540/sshd2 tcp 0 0 *:locker *:* LISTEN autobot 560 536/SERVER tcp 0 0 *:tproxy *:* LISTEN autobot 546 530/ShipTax S tcp 0 0 *:netbios-ssn *:* LISTEN user 456 482/ tcp 0 0 *:616 *:* LISTEN user 400 433/ tcp 0 0 *:npmp-gui *:* LISTEN user 392 433/ udp 0 0 *:sunrpc *:* user 274 330/ raw 208 0 *:icmp *:* 7 user 2017587 - raw 0 0 *:icmp *:* 7 user 0 - raw 0 0 *:tcp *:* 7 user 0 - Active UNIX domain sockets (servers and established) Proto RefCnt Flags Type State I-Node PID/Program name Path unix 1 [ ] STREAM CONNECTED 2021255 16879/httpd @000044ac unix 1 [ ] STREAM CONNECTED 2011633 16877/httpd @00004466 unix 1 [ ] STREAM CONNECTED 1969869 16883/httpd @00004325 unix 1 [ ] STREAM CONNECTED 1965032 16875/httpd @00004307 unix 1 [ ] STREAM CONNECTED 2021003 16878/httpd @000044ab