Slow Script Execution.

Basically my requirement is to know the total number of free anonymous ports.

anonymous port range is 32768- 65535.

i wrote a script for that

**********************************************

for i in {32768..65535}
do
netstat -an | grep $i > /dev/null 
        if [ $? -ne 0 ]
        then
portcount=$((portcount+1))
        fi
done
echo "Totat free anonymous ports are portcount"

************************************************

but the above script take 20 mins to execute

Also tried this way

*******************************************************

netstat -an > $HOME/netstat.out
for i in {32768..65535}
do
grep $i $HOME/netstat.out > /dev/null
        if [ $? -ne 0 ]
        then
portcount=$((portcount+1))
        fi
done
echo "Totat free anonymous ports are:$portcount"

*******************************************************

but still no luck.

Is there a quick way to acheive this ?

sorry wrong context...

Surely you want to start at free ports = 32767 and subtract from there if they are used in netstat?

try the following

ports=$((65535-32768))
for i in $(netstat -an  | grep -Eo '[0-9]+' ) 
   do 
   if [ $i -gt 32768 -a $i -lt 65535 ] 
   then 
      ports=$((ports-1))
   fi
done
echo "There are $ports free "

Try this:

netstat -atu | perl -nae '$F[3]=~/:(\d+)$/;$i++ if $1>32767;END{print "Free anon ports: ",32767-$i,"\n"}'

Note that I used netstat -atu, which will show only TCP and UDP ports, without UNIX sockets present in netstat -an, which might confuse your calculations (6th column in UNIX sockets output shows inode number which might fall into the port range while grepping).

for i in {32768..65535}
do
netstat -an | grep $i > /dev/null 
        if [ $? -ne 0 ]
        then
portcount=$((portcount+1))
        fi
done
echo "Totat free anonymous ports are portcount"

Well no wonder it's slow. You're running netstat, a program which can take seconds to run if you've got a lot of traffic, thirty-two thousand separate times. Just run it once, and process its output once.

I was going to write an awk solution but the perl one looks more elegant.

I'd suggest netstat -atun, to avoid domain unnecessary name lookups. They don't care who's connected to where for this, just what ports are being used.

I wrote it in awk but before submitting saw the sh solution. :wall:

"netstat -atun" is definitely the way to go.

It's not working for me and I never used perl.

bash: netstat -atu | perl -nae '$F[3]=~/:(\d+)$/;$i++ if $1>32767;END{print "Free anon ports: ",32767-$i,"\n"}'

netstat: illegal option -- t
usage: netstat [-anv] [-f address_family]
       netstat [-n] [-f address_family] [-P protocol] [-g | -p | -s [interval [count]]]
       netstat -m [-v] [interval [count]]
       netstat -i [-I interface] [-an] [-f address_family] [interval [count]]
       netstat -r [-anv] [-f address_family|filter]
       netstat -M [-ns] [-f address_family]
       netstat -D [-I interface] [-f address_family]
Free anon ports: 32767

Wanted to share the Operation System version.

bash> uname -a
SunOS mypc 5.10 Generic_144488-07 sun4v sparc SUNW,SPARC-Enterprise-T5220

Also, wanted to understand if you script runs from 32767 to 65535

Thanks for your quick reply.

---------- Post updated at 11:22 AM ---------- Previous update was at 11:11 AM ----------

My grep does not have the E,o option.

grep: illegal option -- E
grep: illegal option -- o

Usage: grep -hblcnsviw pattern file . . .
 
SunOS 5.10          Last change: 26 Feb 2008                    1
User Commands                                             grep(1)
OPTIONS
     The following options are supported for  both  /usr/bin/grep
     and /usr/xpg4/bin/grep:
     -b    Precedes each line by the block number on which it was
           found. This can be useful in locating block numbers by
           context (first block is 0).
     -c    Prints only a count of the lines that contain the pat-
           tern.
     -h    Prevents the name of the file containing the  matching
           line  from  being  prepended  to that line.  Used when
           searching multiple files.
     -i    Ignores upper/lower case distinction during  comparis-
           ons.
     -l    Prints only the names of files  with  matching  lines,
           separated  by NEWLINE characters.  Does not repeat the
           names of files when the pattern  is  found  more  than
           once.
     -n    Precedes each line by its  line  number  in  the  file
           (first line is 1).
     -s    Suppresses error messages about nonexistent or unread-
           able files.
     -v    Prints all lines except those that  contain  the  pat-
           tern.
     -w    Searches for the expression as a word as if surrounded
           by \< and \>.
 
uname -a
SunOS mypc 5.10 Generic_144488-07 sun4v sparc SUNW,SPARC-Enterprise-T5220

I'll make a wild guess and say your netstat doesn't support -t. Try it without it.

It does not take the -u option either.

Tried this but incorrect result.

netstat -a | perl -nae '$F[3]=~/:(\d+)$/;$i++ if $1>32767;END{print "Free anon ports: ",32767-$i,"\n"}'
Free anon ports: 32767

Also worth noting is the output of SunOS's netstat.

[mute@sunny ~]$ netstat -an -f inet

UDP: IPv4
   Local Address        Remote Address      State
-------------------- -------------------- ----------
      *.68                                Idle
      *.546                               Idle
172.30.197.145.68                         Idle
      *.111                               Idle
      *.*                                 Unbound
      *.32771                             Idle
      *.111                               Idle
      *.*                                 Unbound
      *.32775                             Idle
      *.*                                 Unbound
      *.177                               Idle
      *.33173                             Idle

TCP: IPv4
   Local Address        Remote Address    Swind Send-Q Rwind Recv-Q    State
-------------------- -------------------- ----- ------ ----- ------ -----------
      *.*                  *.*                0      0 49152      0 IDLE
127.0.0.1.4999             *.*                0      0 49152      0 LISTEN
      *.111                *.*                0      0 49152      0 LISTEN
      *.*                  *.*                0      0 49152      0 IDLE
      *.111                *.*                0      0 49152      0 LISTEN
      *.*                  *.*                0      0 49152      0 IDLE
      *.22                 *.*                0      0 49152      0 LISTEN
127.0.0.1.5987             *.*                0      0 49152      0 LISTEN
127.0.0.1.898              *.*                0      0 49152      0 LISTEN
127.0.0.1.32774            *.*                0      0 49152      0 LISTEN
127.0.0.1.5988             *.*                0      0 49152      0 LISTEN
127.0.0.1.32775            *.*                0      0 49152      0 LISTEN
      *.5900               *.*                0      0 49152      0 LISTEN
      *.32776              *.*                0      0 49152      0 LISTEN
      *.6001               *.*                0      0 49152      0 LISTEN
      *.5901               *.*                0      0 49152      0 LISTEN
      *.32800              *.*                0      0 49152      0 BOUND
127.0.0.1.6788             *.*                0      0 49152      0 LISTEN
127.0.0.1.6789             *.*                0      0 49152      0 LISTEN
127.0.0.1.32786            *.*                0      0 49152      0 LISTEN
172.30.197.145.22    150.125.197.124.3775 63576     51 49640      0 ESTABLISHED

SCTP:
        Local Address                   Remote Address          Swind  Send-Q Rwind  Recv-Q StrsI/O  State
------------------------------- ------------------------------- ------ ------ ------ ------ ------- -----------
0.0.0.0                         0.0.0.0                              0      0 102400      0  32/32  CLOSED

Some ports are bound to 127.0.0.1 only, some to all interfaces. Should these be counted separately?

#!/bin/sh
netstat -an -f inet | awk '
$1 ~ /\.[0-9]+$/ { p = $1
        while (i = index(p, ".")) p = substr(p, i+1)
        if (0+p >= 32768) a[0+p] = 1 }
/SCTP/ { exit }
END { for (p in a) count++
        print "Ports in use: " count "  Free: " 32768 - count }'

Scott Thanks for your inputs here. Your script runs like a CHAMP !!!

Below is the output based on the copy paste execution of your script.

./"nettest.sh"
Ports in use: 991

Couple of concerns:

1) (I'm naive to unix shell scripting) I am using a parameter input for my script and collecting that in $1.

example
/dumps.sh 4456

I believe my $1 will conflict with the $1 that you use in the script, you shared above.

I changed $1 to $2 at two places in your script but then you script did not yield the correct output.
Can you please help fix this ?

2) Does your script search for the number of used anonymous port which range from 32768- 65535. Because, I dont see 65535 anywhere in your script. HOwever I believe it looks for all ports greater than 32768 that are busy and are 991 in number. Please confirm the same.
I have test your script and have found it to be good, however just wanted to know the logic that you applied to it.

I edited it, try now. The highest legit port number is 65535, so I didn't see a reason to test if it's <65535, because it has to be, right?

no, they're different programs.

I guess if you need that # later? Try it like this:

#!/bin/ksh
free=`netstat -an -f inet | awk '
$1 ~ /\.[0-9]+$/ { p = $1
        while (i = index(p, ".")) p = substr(p, i+1)
        if (0+p >= 32768) a[0+p] = 1 }
/SCTP/ { for (p in a) count++
        print 32768 - count }'`

echo Free ports $free
#!/bin/ksh
free=`netstat -an -f inet | awk '
$2 ~ /\.[0-9]+$/ { p = $2
        while (i = index(p, ".")) p = substr(p, i+1)
        if (0+p >= 32768) a[0+p] = 1 }
/SCTP/ { for (p in a) count++
        print 32768 - count }'`
echo Free ports $free

output is blank no luck this time with the above :frowning:

OUTPUT:

./"nettest.sh"
Free ports

Let me know if i missed anything.

You replaced the $1's with $2's! :confused:

Again, the $1's inside awk have nothing to do with the $1's in your shell. They're in single quotes.

 echo '$1'
$1

So you're feeding a literal $1 into awk, not the script's first parameter, and don't need to change it to $2 to avoid your $1. Awk understands $1 as "the first token in the record".

It does not work with $1 either.

Yes, because this is what i actually do inside the original script that i am trying to incorporate this into.

nettest.sh

#!/bin/ksh
echo "Your PID is $1"
free=`netstat -an -f inet | awk '
$1 ~ /\.[0-9]+$/ { p = $1
        while (i = index(p, ".")) p = substr(p, i+1)
        if (0+p >= 32768) a[0+p] = 1 }
/SCTP/ { for (p in a) count++
        print 32768 - count }'`
echo Free ports $free
echo "Your PID is $1"

output:
./"nettest.sh" 4456
Your PID is 4456
Free ports
Your PID is 4456[/CODE]

I hope I am able to cascade correctly what I am trying to do.

OK maybe I was incorrect in removing the END {} block. Maybe your netstat doesn't have SCTP as mine does. But we'll keep it in there just in case, since i dont use solaris and am unsure if that section comes and goes. heh.

free=`netstat -an -f inet | awk '
$1 ~ /\.[0-9]+$/ { p = $1
        while (i = index(p, ".")) p = substr(p, i+1)
        if (0+p >= 32768) a[0+p] = 1 }
$1 ~ /SCTP/ { exit }
END { for (p in a) count++
        print 32768 - count }'`

echo Free ports $free
1 Like

Hi,

This is what I see with the lastest you shared.

echo "Your PID is $1"
free=`netstat -an -f inet | awk '
$1 ~ /\.[0-9]+$/ { p = $1
        while (i = index(p, ".")) p = substr(p, i+1)
        if (0+p >= 32768) a[0+p] = 1 }
$1 ~ /SCTP/ { exit }
END { for (p in a) count++
        print 32768 - count }'`
echo Free ports $free
echo "Your PID is $1"

 
./"nettest.sh" 4456
Your PID is 4456
Free ports 0
Your PID is 4456

The output is "Free ports 0" even if i don't read my PID using $1.

How about you post what your netstat output looks like so us blind men can stop bothering this elephant?

Here you go:

bash$> netstat -an -f inet
UDP: IPv4
Local Address Remote Address State
-------------------- -------------------- ----------
*.40473 Idle
*.41371 Idle
*.36689 Idle
*.61401 Idle
*.41391 Idle
*.111 Idle
*.* Unbound
*.33067 Idle
*.46783 Idle
*.* Unbound
*.33072 Idle
 
and so on .... scrolling down the line i see
 
*.45175 Idle
*.34185 Idle
TCP: IPv4
Local Address Remote Address Swind Send-Q Rwind Recv-Q State
-------------------- -------------------- ----- ------ ----- ------ -----------
169.81.183.182.58316 169.81.183.188.10055 263536 0 263536 0 CLOSE_WAIT
169.81.183.182.57926 169.81.183.188.10055 263536 0 263536 0 CLOSE_WAIT
169.81.183.182.56846 169.81.183.188.10055 263536 0 263536 0 CLOSE_WAIT
169.81.183.182.55769 169.81.183.188.10055 263536 0 263536 0 CLOSE_WAIT

FYI... the initial script you shared did work.

#!/bin/sh
netstat -an -f inet | awk '
$1 ~ /\.[0-9]+$/ { p = $1
        while (i = index(p, ".")) p = substr(p, i+1)
        if (0+p >= 32768) a[0+p] = 1 }
/SCTP/ { exit }
END { for (p in a) count++
        print "Ports in use: " count "  Free: " 32768 - count }'