Bash exit status when using while loop [duplicate] - linux

This question already has answers here:
How to get the exit status a loop in bash
(8 answers)
Closed 7 years ago.
I have a bash script that goes through a list of ip's and pings them one by one. If the exit status for each ping is 0, then echo that the node is up, else the node is down.I am able to get this to work perfectly, but when the bash script ends the exit status is always 0.
What I am trying to achieve is for example out of 5 ip's if the 3rd one fails, to continue through the list and check the rest but once the script ends throw an exit status other than 0 and output which ip has failed.
cat list.txt | while read -r output
do
ping -o -c 3 -t 3000 "$output" > /dev/null
if [ $? -eq 0 ]; then
echo "node $output is up"
else
echo "node $output is down"
fi
done
thanks in advance!

Your first problem is that by doing cat file | while read you've spawned the while in its own subshell. Any variables it sets will only exist during that loop, so persisting a value will be difficult. More info on that issue here.
If you use while read ... done < file it will work correctly. Make an exit status flag that defaults to zero, but set it to one if any errors occur. Use it as your script's exit value.
had_errors=0
while read -r output
do
ping -o -c 3 -t 3000 "$output" > /dev/null
if [ $? -eq 0 ]; then
echo "node $output is up"
else
echo "node $output is down"
had_errors=1
fi
done < list.txt
exit $had_errors

Related

bash script for run another script after 5 unreachable ping request to specific ip or website

I need script to run another script after 5 consistently 'unreachable' response from ping to specific ip. But if everything okay do nothing. For example for now I have script running ping command by taking ip addresses from text file which has list of ip or websites. And this script run another telegram message sending script if the ip or website from list is unreachable. But it is not good idea because often there can be just 1 unreacable response but overall the website is working or ip is reachable. Now I need the script which runs telegram message sending script after consistently 5 unreachable response. Not after 1 unreachable response. Here's my script:
date
cat /home/user/list.txt | while read output
do
ping -c 1 "$output" > /dev/null
if [ $? -eq 0 ]; then
echo "node $output is up"
else
message="$(echo "node $output is down")"
echo "node $output is down" > /home/user/siteDown.log
telegram-send "$message"
fi
done
Thank to all, have a nice days.
Try this:
#!/bin/sh
try_ping() {
i=0
while [ $((i+=1)) -le 5 ] ; do
ping -c 1 "${1}" > /dev/null \
&& return 0
sleep 0.5
done
return 1
}
date
while read -r output ; do
if try_ping "${output}" ; then
echo "node $output is up"
else
echo "node $output is down" >> /home/user/siteDown.log
telegram-send "node $output is down"
fi
done </home/user/list.txt
I added a 0.5 second sleep after every ping attempt, but you can adjust that.

SSH Remote command exit code

I know there are lots of discussions about it but i need you help with ssh remote command exit codes. I have that code:
(scan is a script which scans for viruses in the given file)
for i in $FILES
do
RET_CODE=$(ssh $SSH_OPT $HOST "scan $i; echo $?")
if [ $? -eq 0 ]; then
SOME_CODE
The scan works and it returns either 0 or (1 for errors) or 2 if a virus is found. But somehow my return code is always 0. Even, if i scan a virus.
Here is set -x output:
++ ssh -i /home/USER/.ssh/id host 'scan Downloads/eicar.com; echo 0'
+ RET_CODE='File Downloads/eicar.com: VIRUS: Virus found.
code of the Eicar-Test-Signature virus
0'
Here is the Output if i run those commands on the "remote" machine without ssh:
[user#ws ~]$ scan eicar.com; echo $?
File eicar.com: VIRUS: Virus found.
code of the Eicar-Test-Signature virus
2
I just want to have the return Code, i dont need all the other output of scan.
!UPDATE!
It seems like, echo is the problem.
The reason your ssh is always returning 0 is because the final echo command is always succeeding! If you want to get the return code from scan, either remove the echo or assign it to a variable and use exit. On my system:
$ ssh host 'false'
$ echo $?
1
$ ssh host 'false; echo $?'
1
$ echo $?
0
$ ssh host 'false; ret=$?; echo $ret; exit $ret'
1
$ echo $?
1
ssh returns the exit status of the entire pipeline that it runs - in this case, that's the exit status of echo $?.
What you want to do is simply use the ssh result directly (since you say that you don't want any of the output):
for i in $FILES
do
if ssh $SSH_OPT $HOST "scan $i >/dev/lull 2>&1"
then
SOME_CODE
If you really feel you must print the return code, that you can do that without affecting the overall result by using an EXIT trap:
for i in $FILES
do
if ssh $SSH_OPT $HOST "trap 'echo \$?' EXIT; scan $i >/dev/lull 2>&1"
then
SOME_CODE
Demo:
$ ssh $host "trap 'echo \$?' EXIT; true"; echo $?
0
0
$ ssh $host "trap 'echo \$?' EXIT; false"; echo $?
1
1
BTW, I recommend you avoid uppercase variable names in your scripts - those are normally used for environment variables that change the behaviour of programs.

How to run bash script while it returns code 0?

I have bash script with many lines of code and I need run it while it returns $? == 0, but in case if it has error I need stop it and exit with code 1?
The question is how to do it?
I tried to use set -e command, but Jenkins does not marks build as failed, for him it looks like Success
I also need to get the Error message to show it in my Jenkins log
I managed to get error code(in my case it will be 126), but how to get error message?
main file
fileWithError.sh
rc=$?; if [[ $rc != 0 ]]; then
echo "exit {$rc} ";
fi
fileWithError.sh
#!/bin/sh
set -e
echo "Test"
agjfsjgfshgd
echo "Test2"
echo "Test3"
Just add the command set -e to the beginning of the file
This should look something similar to this
#!/bin/sh
set -e
#...Your code...
I think you just want:
#!/bin/sh
while fileWithError.sh; do
sleep 1;
done
echo fileWithError.sh failed!! >&2
Note that if the script is written well, then the echo is
redundant as fileWithError.sh should have written a decent
error message already. Also, the sleep may not be needed, but is useful to prevent a fast loop if the script succeeds quickly.
You can get the explicit return value, but it requires a bit of refactoring.
#!/bin/sh
true
while test $? = 0; do fileWithError.sh; done
echo fileWithError.sh failed with status $?!! >&2
since the return value of the while script will be the
return value of sleep in the first construction.
Its not quite easy to get an error code only.
How about this ...
#!/bin/bash
Msg=$(fileWithError.sh 2>&1) # redirect all error messages to stdout
if [ "$?" -ne 0 ] # Not Equal
then
echo "$Msg"
exit 1
fi
exit 0
You catch all messages created by fileWithError.sh and if the programm returned an error code then you have the error message already saved in a variable.
But this will make a disadvantage, because you will temporary store all messages created by fileWithError.sh till the error appears.
You can filter the error message with echo "$Msg" |tail -n 1, but its not 100% save.
You should also do some changes in fileWithError.sh...
Switch set -e with trap "exit 1" ERR. this will close the script on errors.
Hope this will help.

$? in shell script

I'm trying to figure out what this means/how $? gets populated in linux, I tried doing a search but if someone could clarify that would be great:
exitstat=$?
if [ $exitstat -ne 0 ]
then
echo -e "Could Not Extract"
echo -e "Aborting Script `date`"
exit $exitstat
fi
The code above that is:
_xfile << %% 2> /files/thefile-7000.log | _afile -x -r 10 2> /files/thefile-7000.log > /files/thefile.7000
OperatorHigh = $finalnumber
%%
$? expands to the exit status of the most recent foreground command.
Since your prior command is a pipeline, the exit status is that of the last command in the pipeline -- in this case, _afile -- unless the pipefail shell option is set, in which case failures elsewhere in the pipeline can also make exit status nonzero.

Bash: Loop until command exit status equals 0

I have a netcat installed on my local machine and a service running on port 25565. Using the command:
nc 127.0.0.1 25565 < /dev/null; echo $?
Netcat checks if the port is open and returns a 0 if it open, and a 1 if it closed.
I am trying to write a bash script to loop endlessly and execute the above command every second until the output from the command equals 0 (the port opens).
My current script just keeps endlessly looping "...", even after the port opens (the 1 becomes a 0).
until [ "nc 127.0.0.1 25565 < /dev/null; echo $?" = "0" ]; do
echo "..."
sleep 1
done
echo "The command output changed!"
What am I doing wrong here?
Keep it Simple
until nc -z 127.0.0.1 25565
do
echo ...
sleep 1
done
Just let the shell deal with the exit status implicitly
The shell can deal with the exit status (recorded in $?) in two ways, explicit, and implicit.
Explicit: status=$?, which allows for further processing.
Implicit:
For every statement, in your mind, add the word "succeeds" to the command, and then add
if, until or while constructs around them, until the phrase makes sense.
until nc succeeds; do ...; done
The -z option will stop nc from reading stdin, so there's no need for the < /dev/null redirect.
You could try something like
while true; do
nc 127.0.0.1 25565 < /dev/null
if [ $? -eq 0 ]; then
break
fi
sleep 1
done
echo "The command output changed!"

Resources