Help it works but its to SLOW

I am a novice at shell scripting. I have managed to cobble together a script that does exactly what I need it to do. However I am gathering information from 700+ devices. This script takes hours to complete the task. Is there a better way of doing it than what I have listed here? This script processes line by line from the file. Might there be a way to process multiple lines at the same time? Any help would be great.

I would also like to hear feed back about the script itself. How did I do for my first unix script, what would make it better.
I have posted the working script below. (community strings changed of course)

NOTE: Has to be shell script, Perl is on the box but I don't have access to it.

#

#======================================================================#
#This section tests for the file device.log, and removes if it exists  #
#======================================================================#
if [ device.log ]
then
rm device.log
fi
cat adoa.txt | while read device              
do
#======================================================================#
#This section will pull the ip address from DNS and assign the value   #
#To the variable "ip".  It will then test to see if the variable "ip"  #
#Has a Null (value of no length) or if it contains data. If a value is #
#Found it is stored in the variable "dns" If no data is found in the   #
#variable "ip" it will assign the string value of "Not in DNS"  to the #
#variable "dns"                                                        #
#======================================================================#
#
	ip=`(host $device | awk ' /has/ {print $4}')` 
	if [ "$ip" ]
	then 
		dns="$ip"
#
#		
#
#
#======================================================================#
#This section will Ping the variable "device" and record a yes or no   #
#Answer in the variable "routable". Error messages such as unknown     #
#Host are sent to /dev/null to prevent them from displaying            #
#======================================================================#
#
	answer=`(ping -c 1 -q -w 2 -n  $device 2>/dev/null | grep received | awk '{print $4}')`
	if
        [ $answer = 0 ];
        then answer="NO"
        else answer="YES"
        fi
#
#======================================================================#
#This section will test the read and read/write snmp community strings #
#Yes or No answer is stored in the variable "snmpread" and "snmpwrite" #
#This section also collects the device system name and stores it in    #
#the variable "routername"
#======================================================================#
#
	if
	[ $answer = "YES" ];
        then

	routername=`(snmpget -t 2 -r 0 -Oqv -Os -v 2c -c (Read only community string) $device 2>/dev/null  SNMPv2-MIB::sysName.0)`
        if [ "$routername" = "" ];
        then snmpread="NO"
	routername="N/A"
        else snmpread="YES"
        fi
        snmpwrite=`(snmpget -t 2 -r 0 -Oqv -Os -v 2c -c (Read Write community string) $device 2>/dev/null  SNMPv2-MIB::sysName.0)`
        if [ "$snmpwrite" = "" ];
        then snmpwrite="NO"
        else snmpwrite="YES"
        fi

if [ "$snmpwrite" = "YES" ];
then
devicenumber=`(snmpwalk -v 2c -OQs -c  (Read Write community string) $device  1.3.6.1.2.1.4.20.1.2 | grep $ip | awk ' /=/ {print $3}')`
interface=`(snmpwalk -v 2c -OQs -c  (Read Write community string) $device  1.3.6.1.2.1.2.2.1.2 | grep -w ifDescr.$devicenumber | awk '/=/ {print $3}')`
else
interface="N/A"
fi
#======================================================================#
#this section takes the values of the variables and outputs them to    #
#the file device.log and echos the values on the screen as well        #
#this section is for the ouput of completed script                     #
#======================================================================#
echo "$device,$routername,$dns,$answer,$snmpread,$snmpwrite,$interface" >> device.log 
echo "$device,$routername,$dns,$answer,$snmpread,$snmpwrite,$interface"

	

#
#======================================================================#
#when device is not reachable by  ping this section assigns values to  #
#variables not tested and exits script                                 #
#======================================================================#
#
        
          else
                snmpread="NO"
                snmpwrite="NO"
		interface="N/A"
		routername="N/A"
echo "$device,$routername,$dns,$answer,$snmpread,$snmpwrite,$interface" >> device.log
echo "$device,$routername,$dns,$answer,$snmpread,$snmpwrite,$interface"

fi

#
#=====================================================================#
# If device is not is DNS this section assigns values to variables    #
# Not tested and exits script                                         #
#=====================================================================#
#

 else
                dns="Not in DNS"
                answer="NO"
                snmpread="NO"
                snmpwrite="NO"
		interface="N/A"
		routername="N/A"
echo "$device,$routername,$dns,$answer,$snmpread,$snmpwrite,$interface" >> device.log
echo "$device,$routername,$dns,$answer,$snmpread,$snmpwrite,$interface"
  
fi


done

I don't think the condition is useful, you probably mean if [ -e device.log ] or some such but you might as well just unconditionally rm -f device.log

Well, here's our old friend the Useless Use of Cat. This is a fairly benign case, but as a stylistic issue, you might prefer while read device; do ... done <adoa.txt

Again as a stylistic issue, grep | awk is a bit of an abomination, because awk has a built-in grep. Try awk '/received/ { print $4 }'

As for the SNMP stuff, would it be more efficient to collect all the data and then at the end commit the result to SNMP in one big transaction? I don't have much experience with that so this is pure speculation.

You could run the pings asynchronously, that's probably the big bottleneck here. There was a very brief thread about that here within the last week or so; host discovery using bash

Hope this helps.

Oh, and to reduce code duplication, you could use "tee -a device.log" here. Also, since this recurs a number of times, maybe you could break it out into its own little function. That would be my main stylistic suggestion actually -- see which parts you could modularize into functions.

I will look into your suggestions. I am sure my "style" Issues will improve with use. SNMP is a large monster in itself. The ping is one of the bottle necks but the information being requested via snmp is another. Each device in the list takes 15 to 30 seconds to process. Taking the larger number that is about 350 minutes to process all devices. I was hoping there is a way to process say 10 devices at once. This may be too advanced for a shell script. My only experience at programming is from Turbo Pascal 15 years ago in high school, so I am a bit out of date.

Thank you again for all your help. So over all its not a completely horrific example of a script? LOL

It's been a long time since I worked with SNMP so can't really help there. Being able to batch all the commands would probably speed it up a little bit, as you spend a significant amount of time just handshaking when you do them one by one.

Another thing I noticed just now: the parentheses inside the backticks are redundant, they force a subshell to be spawned within the backticks for no good reason.

No, it's a pretty decent script. Beginner scripts are often ways too overcomplicated but this one is straightforward and you don't seem too confused about how to do things. If you can avoid the backticks then that's probably a design improvement, but that probably requires you to find a different SNMP command.

Garlandxj, your script is pretty decent, besides the minor issues noted by "era".
The duration of the whole operation is probably, as you said, because you are collecting data from 700+ devices.
You also said that you don't have access to perl on the box - if you are granted with such, in the future, you might consider Perl:SNMP module, this could speed up the things a little bit, but not too much, since you are dependnt on SNMP binary itself.

A few minor changes
I wrote a small script that calls the other as a back ground process.

cat devices.txt | while read device
do

export device
jobs
( devicev2 &)
done

This actually works wonderful. I Get the data from all 700 devices in under 1 minute. Closer to 35 seconds. There is a down side however. Both processors spike to 90 % utilization. Not to bad since it processes all the data so quickly. I have purchased a book on shell scripting and hopefully my "Style" will improve quickly.

One quick question: To limit this to 100 devices per batch (to keep from killing the processors) how would i go about doing that. I have been playing all night with different coding and I seem to get it wrong every time. A nudge in the right direction would be great.

Thanks again for all your help

How about keep a count and sleep for a while if the count is exactly a multiple of 100? Or even, wait for all the processes you have spawned so far.

Also have a look at the nice and (if you have it) ionice commands. Those can be used to tell the scheduler to give other processes priority over this one, but if there are none others competing for resources, still let you run at full speed.

As an aside, as always, anything involving the cat of a single file is usually better phrased with redirection.

while read device
do
  .... stuff
done <devices.txt

Thank you for the reply. Hopefully with practice I won't have to ask such basic questions.

The thought I have in my head is a count

I have been playing with different ones i have found online and I can't get them to work. I am sure it is me that is the problem.

The only purpose of the first script file is to cat the file device.txt and store the variable $device then export that to the second script file i call as a back ground process.

What I am hoping for but cannot get the syntax correct is

as the first script launches background processes it starts a count of how many it has started. Once it reaches 100 it either waits for all background processes to finish (preferable) then continues on. OR

it sleeps for 120 seconds before firing off the next hundred.

As for the cat the output redirect is inside the script that is called

named devicev2

ip=`(host $device | awk ' /has/ {print $4}')`

    if [ "$ip" ]
    then
            dns="$ip"

answer=`(ping -c 1 -q -w 2 -n $device 2>/dev/null | grep received | awk '{print $4}')`
if
[ $answer = 0 ];
then answer="NO"
else answer="YES"
fi

if
[ $answer = "YES" ];
then

    routername=\`\(snmpget -t 2 -r 0 -Oqv -Os -v 2c -c \(Read only community string\) $device 2&gt;/dev/null  SNMPv2-MIB::sysName.0\)\`
    if [ "$routername" = "" ];
    then snmpread="NO"
    routername="N/A"
    else snmpread="YES"
    fi
    snmpwrite=\`\(snmpget -t 2 -r 0 -Oqv -Os -v 2c -c \(Read Write community string\) $device 2&gt;/dev/null  SNMPv2-MIB::sysName.0\)\`
    if [ "$snmpwrite" = "" ];
    then snmpwrite="NO"
    else snmpwrite="YES"
    fi

if [ "$snmpwrite" = "YES" ];
then
devicenumber=`(snmpwalk -v 2c -OQs -c (Read Write community string) $device 1.3.6.1.2.1.4.20.1.2 | grep $ip | awk ' /=/ {print $3}')`
interface=`(snmpwalk -v 2c -OQs -c (Read Write community string) $device 1.3.6.1.2.1.2.2.1.2 | grep -w ifDescr.$devicenumber | awk '/=/ {print $3}')`
else
interface="N/A"
fi

echo "$device,$routername,$dns,$answer,$snmpread,$snmpwrite,$interface" >> device.log
echo "$device,$routername,$dns,$answer,$snmpread,$snmpwrite,$interface"

   else
            snmpread="NO"
            snmpwrite="NO"
            interface="N/A"
            routername="N/A"

echo "$device,$routername,$dns,$answer,$snmpread,$snmpwrite,$interface" >> device.log
echo "$device,$routername,$dns,$answer,$snmpread,$snmpwrite,$interface"

fi

else
dns="Not in DNS"
answer="NO"
snmpread="NO"
snmpwrite="NO"
interface="N/A"
routername="N/A"
echo "$device,$routername,$dns,$answer,$snmpread,$snmpwrite,$interface" >> device.log
echo "$device,$routername,$dns,$answer,$snmpread,$snmpwrite,$interface"

fi

exit

Sorry Era,

I missed what you were saying about the redirection.
I was thinking output you were talking about removing cat all together and redirecting the input to read from the file.

That is something that will change along with the redundant echo statements. I will work on making it stylish once I get it working to my liking. I do have access to the nice command and I will play with that. I am wanting to throttle it with a count though. This is a remote box, and getting someone in Arizona to reboot it if I crash it can be time consuming. This box has our regular monitoring tools on it as well. I want to be very sure I am not draining the resources away from things that are vital.

The shell is not very good at arithmetic, so for small numbers, I prefer working with just a string -- grow the string by one character in the loop, and once the string is x characters long, quit. But here, obviously, the number is large enough that the traditional way of doing this is better.

n=0
while whatever; do
  n=`expr 1 + $n`
  case $n in *00) wait;; esac
  ... go on
done