LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   infinite nested while loop (https://www.linuxquestions.org/questions/programming-9/infinite-nested-while-loop-4175437187/)

wolverene13 11-14-2012 06:15 PM

infinite nested while loop
 
Hi,

After much searching, I've decided to resort to the tried and true LQ Forums. Here's what I'm trying to accomplish:

I need to ping several nodes one at a time with a single ping, over and over. When it reaches the bottom of the list, I want it to go back to the top and do it over again, infinitely. Here's what I tried so far:

Code:


#!/bin/bash

while true
do
#useless use of cat, I know
cat ~/tunnel_ips.txt | while read NODE
do
rc=$?
ping -c 1 -w 1 $NODE > /dev/null
if [ $rc -eq 0 ]; then
echo "$NODE alive"
else
echo "$NODE failed"
fi
done >> pingscript-output.txt
done

This appears to work, however, it created a bit of a problem where the CPU was spiking after a while. I investigated and found out that the script didn't work the way I thought it would, as it basically created multiple instances of the script process over and over again without killing the previous one as I assumed it would. So, I ended up slamming the server. I know it's because of the nesting of the while loops, combined with the "while true" statement, but I can't really think of another way to do it. Anyone got any ideas?

Thanks in advance,

wolverene13

towheedm 11-14-2012 07:53 PM

I busted my brains trying to figure out where your script calls itself. Finally, decided to run it with one modification. The variable $? returns the exit status of the last command, or according to BASH's textinfo pages:
Quote:

? Expands to the exit status of the most recently executed fore‐
ground pipeline.
As it is in your script it will return the exit status of the read NODE command. Moving it to after the ping command and running your script did not create (according to top) any additional processes.

You can also do away with the if-then statement by using the && and || commands. Also redirect both stderr and stdout to /dev/null.

Here my modified script:
Code:

#!/bin/bash

while true; do
  while read NODE ; do
    ping -c 1 -w 1 $NODE &>/dev/null && echo "$NODE alive" || echo "$NODE failed"
  done < tunnel_ips.txt >> pingscript-output.txt
done
exit

Or, if you must use if-then:
Code:

if ping -c 1 -w 1 $NODE &>/dev/null ; then
  echo "$NODE alive"
else
  echo "$NODE failed"
fi

I would also recommend a timestamp:
Code:

echo "$(date +%H":"%M) $NODE alive"

wolverene13 11-14-2012 08:04 PM

Quote:

Originally Posted by towheedm (Post 4829661)
I busted my brains trying to figure out where your script calls itself. Finally, decided to run it with one modification. The variable $? returns the exit status of the last command, or according to BASH's textinfo pages:


As it is in your script it will return the exit status of the read NODE command. Moving it to after the ping command and running your script did not create (according to top) any additional processes.

You can also do away with the if-then statement by using the && and || commands. Also redirect both stderr and stdout to /dev/null.

Here my modified script:
Code:

#!/bin/bash

while true; do
  while read NODE ; do
    ping -c 1 -w 1 $NODE &>/dev/null && echo "$NODE alive" || echo "$NODE failed"
  done < tunnel_ips.txt >> pingscript-output.txt
done
exit

Or, if you must use if-then:
Code:

if ping -c 1 -w 1 $NODE &>/dev/null ; then
  echo "$NODE alive"
else
  echo "$NODE failed"
fi

I would also recommend a timestamp:
Code:

echo "$(date +%H":"%M) $NODE alive"

Awesome! Both solutions worked perfectly! Thanks! I'm confused though - when using the || operator, how does it know whether to echo failed or alive?

towheedm 11-14-2012 08:32 PM

From BASHs info pages:
Quote:

Lists
A list is a sequence of one or more pipelines separated by one of the
operators ;, &, &&, or ⎪⎪, and optionally terminated by one of ;, &, or
<newline>.

Of these list operators, && and ⎪⎪ have equal precedence, followed by ;
and &, which have equal precedence.

A sequence of one or more newlines may appear in a list instead of a
semicolon to delimit commands.

If a command is terminated by the control operator &, the shell exe‐
cutes the command in the background in a subshell. The shell does not
wait for the command to finish, and the return status is 0. Commands
separated by a ; are executed sequentially; the shell waits for each
command to terminate in turn. The return status is the exit status of
the last command executed.

AND and OR lists are sequences of one of more pipelines separated by
the && and ⎪⎪ control operators, respectively. AND and OR lists are
executed with left associativity. An AND list has the form

command1 && command2

command2 is executed if, and only if, command1 returns an exit status
of zero.

An OR list has the form

command1 ⎪⎪ command2

command2 is executed if and only if command1 returns a non-zero exit
status. The return status of AND and OR lists is the exit status of
the last command executed in the list.
So basically:
Code:

command1 && command2 || command3
If command1 returns an exit status of 0, command2 gets executed and the lists ends.
It becomes:
Code:

command1 && command2
If command1 returns an exit status other than 0, then command2 does not get executed. Instead command3 gets executed:
Code:

command1 || command3
So basically:
Code:

command1 && command2 || command3
says: If command1 succeeds then exucute command2 else execute command3, which is why it's the same as:
Code:

if command1 ; then
  command2
else
  command3
fi

Hope I explained it properly.


All times are GMT -5. The time now is 04:55 AM.