Hayl_ has asked for the wisdom of the Perl Monks concerning the following question:

I have the following code - which strings together nmap, fuser, and ps and gives me pretty output. The problem I have is that some subscripts of @pid array contain a list instead of a scalar so when it prints I get strangely formatted output.
use strict; # Variables my @nmap; my @port; my @protocol; my @pid; my @app; my $junk; my $i; print "\nScanning for open ports... "; chomp(@nmap = `nmap localhost`); for (1..5) { shift(@nmap); } for (1..2) { pop(@nmap); } $i = 0; foreach (@nmap) { if (! @nmap[$i] =~ /^\s*$/) { (@port[$i], @protocol[$i]) = split /\//, $_; @protocol[$i] = split / /, @protocol[$i]; @pid[$i] = `fuser -n @protocol[$i] @port[$i]`; chomp(($junk, @pid[$i]) = split /:\s+/,@pid[$i]); @app[$i] = `/bin/ps --no-headers -o %c @pid[$i]`; $i++; } } print "done.\n\n"; print "The following ports are open on your system\n\n"; printf "%8s%12s%12s%15s\n","Port", "Protocol", "PID", "App"; print "-----------------------------------------------\n"; $i = 0; foreach (@nmap) { printf "%8s%12s%12s%16s\n", @port[$i], @protocol[$i], @pid[$i][1], + @app[$i]; $i++; }
good looking output where there is only one PID per port:
Scanning for open ports... done. The following ports are open on your system Port Protocol PID App ----------------------------------------------- 22 tcp 21232 sshd 515 tcp 21943 lpd 6000 tcp 1738 X
bad looking output with multiple PIDs per port:
Scanning for open ports... done. The following ports are open on your system Port Protocol PID App ----------------------------------------------- 25 tcp 198 exim 53 tcp 226 named 139 tcp242 3995 3997 4007 4009 4011 smbd 143 tcp 269 1006 xinetd
I think I know what I want to do but I don't know how to do it. I want to print the first item in the @pid$i list and then have a foreach loop to print the rest after the first line.

Replies are listed 'Best First'.
Re: @pid = a list - need a way to print
by CombatSquirrel (Hermit) on Aug 22, 2003 at 13:46 UTC
    Substitute the lines
    foreach (@nmap) { printf "%8s%12s%12s%16s\n", @port[$i], @protocol[$i], @pid[$i][1], +@app[$i]; $i++; }
    by
    foreach (@nmap) { my @pids = @pid[$i][1]; printf "%8s%12s%12s%16s\n", @port[$i], @protocol[$i], shift(@pids), + @app[$i]; for (@pids) { printf " " x 20 . %12s" . " " x 16 . "\n", shift(@pids); } ++$i; }
    I'm not using Unix, so I can't test it, but it should work.
    Cheers, CombatSquirrel.
      thanks! you pointed me in the right direction. i ended up with this code:
      #!/usr/bin/perl # # App Name: pscan.pl # Written By: DKD (dale_d at telusplanet dot net) # # uses nmap, fuser, and ps to display the port number, # protocol, pid, and executable name for each open # port in a readable format. # # Requires: nmap, fuser, ps # # Usage: pscan.pl # No switches use strict; # Variables my @temp; my @nmap; my @port; my @protocol; my @pid; my @pids; my @app; my $whoami; my $junk; my $i; # Check to see if you are root, and then display some text. $whoami = `whoami`; chomp($whoami); die "You must run pscan as root!\n" unless ( $whoami eq "root"); print "\nScanning for open ports."; # Grab input from nmap and discard any output lines that we # don't want (any that don't start with numeric characters). chomp(@temp = `nmap localhost`); foreach (@temp) { push(@nmap, $_) if $_ =~ /^\d+/; } # Iterate through the output from nmap and extract the port # and protocol, then use the port and protocol to get the # PID from the fuser command, then use ps to get the name of # the executable from the pid. $i = 0; foreach (@nmap) { (@port[$i], @protocol[$i]) = split /\//, $_; @protocol[$i] = split / /, @protocol[$i]; @pid[$i] = `fuser -n @protocol[$i] @port[$i]`; chomp(($junk, @pid[$i]) = split /:\s+/,@pid[$i]); chomp(@app[$i] = `/bin/ps --no-headers -o %c @pid[$i]`); $i++; print "."; } # Display the data in a readable format print "done." . "\n" x 2; # print the header print " The following ports are open on your system:\n\n"; printf "%8s%12s%12s%16s\n","Port", "Protocol", "PIDs", "Process"; print "-" x 48 . "\n"; $i = 0; foreach (@nmap) { # print the port, protocol, first pid, and app(proc) chomp(@pids = split / +/, @pid[$i]); printf "%8s%12s%12s%16s\n", @port[$i], @protocol[$i], shift(@pids) +, @app[$i]; foreach (@pids) { # print any pids if there are more than one. printf "%32s\n", $_; } print " -" x 24 ."\n"; # dashed line separating each port ++$i; }