I want to run a bash-script on my raspi.
The script's purpose is to check one device in my network for availability (with ping).
If this device is responding, script should end.
If it's not responding it has to go further and check the availability of 3 specific devices: if one device of that 3 is responding, then send a mail; if none of these devices is responding, then do nothing.
I hope it is recognizable what I've done so far:
#!/bin/bash
array=(192.168.xxx.xxx 192.168.xxx.xxx)
ping -c 1 192.168.xxx.xxx
if [$? -eq 0]; then exit 0
else
for devices in "${array[#]}"
do ping -c 1 $devices &> /dev/null
if [ $? -eq 0 ]; then exit 0
fi
fi
done
/usr/sbin/sendmail foo#bar.com < /home/pi/scripts/email.txt
I'm pretty stuck right now, because my script skills are frightening bad.
Some comments:
#!/bin/bash
array=(192.168.xxx.xxx 192.168.xxx.xxx)
# a way to simplify first if:
ping -c 1 192.168.xxx.xxx && exit 0
for devices in "${array[#]}"; do
# you want send mail if ping is ok
if ping -c 1 $devices &> /dev/null; then
/usr/sbin/sendmail foo#bar.com < /home/pi/scripts/email.txt
exit 0
fi
done
Two errors in your code:
if [$? -eq 0]; then should be if [ $? -eq 0 ]; then
The fi before done should be moved outside the for loop.
Example:
array=(192.168.xxx.xxx 192.168.xxx.xxx)
ping -c 1 192.168.xxx.xxx
if [ $? -eq 0 ]; then
exit 0
else
for devices in "${array[#]}";do
ping -c 1 $devices &> /dev/null
if [ $? -eq 0 ]; then
exit 0
fi
done
fi
Suggested improvements:
Double quoting your variables is a good practice
Use of if [[ $? -eq 0 ]]; then is better than if [ $? -eq 0 ]; then in bash
Related
I'm really sorry to annoy to you again with my problem but it seems I'm about to finish. My goal is to create a bash-script that checks if a IP-address is still online or a scrub is in progress and if not that my systems shuts down. My script, which is currently in use, looks like this
#!/bin/bash
hosts=(
10.10.0.100 #Client 1
10.10.0.101 #Client 2
10.10.0.102 #Client 3
10.10.0.103 #Client 4
10.10.0.104 #Client 5
)
for host in "${hosts[#]}"; do
if ping -c 1 -i 1 "$host" >/dev/null; then
echo "No Shutdown - At least one PC ($host) is online"
exit 0
fi
done
echo "No PC is online - Shutdown"
bash shutdown -p now
I did some research and found the following command, to check if my scrub is in progress
if [ $(zpool status | grep 'scrub in progress') ]; then
echo "No Shutdown - Scrub in progess"
exit 0
fi
But i have problems in combining these two. I want my script to first check the IPs and if they all are offline then check for a scrub before it shuts down the machine. So both if-cases have to be false (ips offline and scrub not in progress) but they should be processed chronological and if the first if-case returns a IP which is online the script should stop.
Maybe somebody can help me?
To check if a command output contains a string, just:
if zpool status | grep -q 'scrub in progress'; then
The [ $(zpool status | grep 'scrub in progress') ] is invalid. The $( .. ) will expand to multiple words and will run [ scrub in progress ]. Because in is not a valid operator for [, then [ will print an error message and exit with 2. Just check the error status of grep.
For me, the answer is trivial:
hosts=(
10.10.0.100 #Client 1
10.10.0.101 #Client 2
10.10.0.102 #Client 3
10.10.0.103 #Client 4
10.10.0.104 #Client 5
)
for host in "${hosts[#]}"; do
if ping -c 1 -i 1 "$host" >/dev/null; then
echo "No Shutdown - At least one PC ($host) is online"
exit 0
fi
done
if $(zpool status | grep 'scrub in progress'); then
echo "No Shutdown - Scrub in progess"
exit 0
fi
echo "No PC is online and Scrub is not in progress - Shutdown"
shutdown -p now
Or did I miss the point?
Mind a couple of corrections inside of your code: remove the square brackets around the if test and remove the bash before shutdown.
You can negate the exit status by using !
if ! ping -c 1 -i 1 "$host" >/dev/null; then
if ! [[ $(zpool status | grep 'scrub in progress') ]]; then
echo "No PC is online - Shutdown"
bash shutdown -p now
fi
fi
## Add the rest of the script here if both conditions are true.
That basically means both condition are false, meaning there is no host up and scrub is not in progress, the opposite without the !
Check if both conditions are true just remove the !
if ping -c 1 -i 1 "$host" >/dev/null; then
if [[ $(zpool status | grep 'scrub in progress') ]]; then
echo "No Shutdown - At least one PC ($host) is online"
exit 0
fi
fi
Check if hosts are up but scrub is not running.
if ping -c 1 -i 1 "$host" >/dev/null; then
if ! [[ $(zpool status | grep 'scrub in progress') ]]; then
##: Add/run/execute your code here to start scrub.
fi
fi
If Nesting is what you want that would be something like.
for host in "${hosts[#]}"; do
if ping -c 1 -i 1 "$host" >/dev/null; then
echo "No Shutdown - At least one PC ($host) is online"
exit 0
elif ! ping -c 1 -i 1 "$host" >/dev/null; then
if ! [[ $(zpool status | grep 'scrub in progress') ]]; then
echo "No PC is online - Shutdown"
bash shutdown -p now
fi
fi
done
You can add the test for zpool inside the first if-statement.
for host in "${hosts[#]}"; do
if ping -c 1 -i 1 "$host" >/dev/null; then
if [[ $(zpool status | grep 'scrub in progress') ]]; then
echo "No Shutdown - At least one PC ($host) is online"
exit 0
fi
fi
elif ! ping -c 1 -i 1 "$host" >/dev/null; then
if ! [[ $(zpool status | grep 'scrub in progress') ]]; then
echo "No PC is online - Shutdown"
bash shutdown -p now
fi
fi
done
The first example set of if-statement should be enough though.
see help test
I have a little problem with my bash script
i got a school project where i have to make a bash script to check if the host is up every 5 minuttes and if fails send email
i had problems with the "fi" statement but fixed the error.
now when i run the script i get an error: line 17 to many arguments"
it initiate the ping command (my Anti virus is blocking the ICMP, so i know the ping lines work)
#!/bin/bash
#Server-status script
FAILS=0
EMAIL_ADDRESS="Critical-error#gruppe4.net" ##Email capabilities
SERVER="192.168.1.1" ###Host to check
SLEEP=300 ###Defining Sleep
while true; do
ping -c 1 $SERVER >/dev/null 2>&1
if [ $? -ne 0 ] ; then #if ping exits nonzero...
FAILS=$"[FAILS + 1]"
else
FAILS=0
fi
if [ $FAILS -gt 4 ]; then
FAILS=0
echo "Server $SERVER is offline!" \
| mail -s "Server offline" "$EMAIL_ADDRESS"
fi
sleep $SLEEP #check again in SLEEP seconds
done
use declare -i to use FAILS as integer and initialize to 0
declare -i FAILS=0
then sum 1
FAILS=$FAILS+1
here is my code(I've commented the mail commmand):
#!/bin/bash
#Server-status script
declare -i FAILS=0
EMAIL_ADDRESS="Critical-error#gruppe4.net" ##Email capabilities
SERVER="192.168.1.1" ###Host to check
SLEEP=1 ###Defining Sleep
echo "1-FAILS[$FAILS]"
while true; do
ping -c 1 $SERVER >/dev/null 2>&1
if [ $? -ne 0 ] ; then #if ping exits nonzero...
FAILS=$FAILS+1
else
FAILS=0
fi
echo "2-FAILS[$FAILS]"
if [ $FAILS -gt 1 ]; then
FAILS=0
echo "Server $SERVER is offline!" # \ | mail -s "Server offline" "$EMAIL_ADDRESS"
fi
sleep $SLEEP #check again in SLEEP seconds
done
output:
sh-4.3$ bash -f main.sh
1-FAILS[0]
2-FAILS[1]
2-FAILS[2]
Server 192.168.1.1 is offline!
2-FAILS[1]
2-FAILS[2]
Server 192.168.1.1 is offline!
2-FAILS[1]
2-FAILS[2]
Server 192.168.1.1 is offline!
2-FAILS[1]
I hope this can help
Regards
Claudio
You are expanding the $FAILS variable with content [FAILS + 1], getting an invalid [] syntax.
Change FAILS=$"[FAILS + 1]" to:
FAILS=$((FAILS+1))
I have a bash script that connects to servers via SSH to run a command. The script gets the IP address from a file.
Problem: if I have 500 IPs in the file, I don't want to simultaneously open or try to open 500 connections. I want to do, lets say, 10 at a time in order to save resources.
How do I run the command via SSH 10 servers at a time?
Here is my script:
#/bin/bash
nodes="big_list_of_nodes.txt"
while read node; do
# Running in background
(uptime=`ssh -o ConnectTimeout=5 $node uptime 2>&1`
if [ $? -eq 0 ]; then
echo "$node uptime: $uptime"
else
echo "Connection timeout for node $node"
fi) &
done < $nodes
# Wait for all jobs to finish
wait
You want to write a function to do all the work for you that takes an IP address as an argument. Then use parallel to read in the file and distribute work to the function:
function get_uptime()
{
node=$1
uptime=`ssh -o ConnectTimeout=5 $node uptime 2>&1`
if [ $? -eq 0 ]; then
echo "$node uptime: $uptime"
else
echo "Connection timeout for node $node"
fi
}
export -f get_uptime
parallel -j 10 --will-cite -a big_list_of_nodes.txt get_uptime
The -j argument tells parallel how many jobs can be active at a time.
I was able to figure it out and make it work.
I add N lines to an array, then I process everything in that array. Then the array is empty and the process is repeated.
This way, you can have a file with hundreds of hostnames or IP address and process in N chunks.
#/bin/bash
nodes=`cat big_list_of_nodes.txt`
for node in $nodes
do
array+=($node)
if [ ${#array[#]} -gt 10 ]; then
for n in ${array[#]}
do
(uptime=`ssh -o ConnectTimeout=5 $n uptime 2>&1`
if [ $? -eq 0 ]; then
echo "$n uptime: $uptime"
else
echo "Connection timeout for node $n"
fi) &
done
wait
array=()
fi
done
if [ ${#array[#]} -gt 0 ]; then
for n in ${array[#]}
do
(uptime=`ssh -o ConnectTimeout=5 $n uptime 2>&1`
if [ $? -eq 0 ]; then
echo "$n uptime: $uptime"
else
echo "Connection timeout for node $n"
fi) &
done
wait
fi
I am working on a small script that checks if a host is up or down.
until [ "$STATUS" -eq "0" ]
do
ping -c 1 192.168.0.3
echo The host is down
STATUS=`echo $?`
done
It is supposed to change the status to 0 if it pings a host that is up and exit the until loop. But it doesnt. Even if I echo out the value of $? the value is always zero.
Can anyone help me figure this out please? :)
Thanks in advance
You have echo The host is down after ping command. So $? takes the exit status of the echo command not the ping command.
ping -c 1 192.168.0.3
STATUS=$?
if [ $STATUS -ne 0 ]; then
echo "The host is down"
fi
You placed echo after saving the status that's why you always get 0:
ping -c 1 192.168.0.3
echo The host is down ## Always changes $? to 0
STATUS=`echo $?`
One better way to do it could be:
until ping -c 1 192.168.0.3; do
echo "The host is down"
done
Longer version:
until ping -c 1 192.168.0.3; STATUS=$?; [ "$STATUS" -eq "0" ]; do
echo "The host is down"
done
I'm thinking that this needs to be changed to a while clause, at the moment it'll wait till all 10000 pings are done, I need it to return when the ping is successful. The program "say" is on OSX it makes the computer speak.
#!/bin/bash
echo begin ping
if ping -c 100000 8.8.8.8 | grep timeout;
then echo `say timeout`;
else echo `say the internet is back up`;
fi
OK I don't have rights to answer my own question so here's my answer for it after playing around:
Thanks, yeah I didn't know about $? until now. Anyway now I've gone and made this. I like that yours doesn't go forever but in my situation I didn't need it to stop until it's finished.
#!/bin/bash
intertube=0
echo "begin ping"
while [ $intertube -ne 1 ]; do
ping -c 3 google.com
if [ $? -eq 0 ]; then
echo "ping success";
say success
intertube=1;
else
echo "fail ping"
fi
done
echo "fin script"
You probably shouldn't rely on textual output of a command to decide this, especially when the ping command gives you a perfectly good return value:
The ping utility returns an exit status of zero if at least one response was heard from the specified host; a status of two if the transmission was successful but no responses were received; or another value from <sysexits.h> if an error occurred.
In other words, use something like:
((count = 60)) # Maximum number to try.
while [[ $count -ne 0 ]] ; do
ping -c 1 8.8.8.8 # Try once.
rc=$?
if [[ $rc -eq 0 ]] ; then
((count = 1)) # If okay, flag loop exit.
else
sleep 1 # Minimise network storm.
fi
((count = count - 1)) # So we don't go forever.
done
if [[ $rc -eq 0 ]] ; then # Make final determination.
echo `say The internet is back up.`
else
echo `say Timeout.`
fi
You don't need to use echo or grep. You could do this:
ping -oc 100000 8.8.8.8 > /dev/null && say "up" || say "down"
This can also be done with a timeout:
# Ping until timeout or 1 successful packet
ping -w (timeout) -c 1
I use this Bash script to test the internet status every minute on OSX
#address=192.168.1.99 # forced bad address for testing/debugging
address=23.208.224.170 # www.cisco.com
internet=1 # default to internet is up
while true;
do
# %a Day of Week, textual
# %b Month, textual, abbreviated
# %d Day, numeric
# %r Timestamp AM/PM
echo -n $(date +"%a, %b %d, %r") "-- "
ping -c 1 ${address} > /tmp/ping.$
if [[ $? -ne 0 ]]; then
if [[ ${internet} -eq 1 ]]; then # edge trigger -- was up now down
echo -n $(say "Internet down") # OSX Text-to-Speech
echo -n "Internet DOWN"
else
echo -n "... still down"
fi
internet=0
else
if [[ ${internet} -eq 0 ]]; then # edge trigger -- was down now up
echo -n $(say "Internet back up") # OSX Text-To-Speech
fi
internet=1
fi
cat /tmp/ping.$ | head -2 | tail -1
sleep 60 ; # sleep 60 seconds =1 min
done
If you use the -o option, BSD ping (which is also on macOS) will exit after receiving one reply packet.
Further reading: https://www.freebsd.org/cgi/man.cgi?query=ping
EDIT: paxdiablo makes a very good point about using ping’s exit status to your advantage. I would do something like:
#!/usr/bin/env bash
echo 'Begin ping'
if ping -oc 100000 8.8.8.8 > /dev/null; then
echo $(say 'timeout')
else
echo $(say 'the Internet is back up')
fi
ping will send up to 100,000 packets and then exit with a failure status—unless it receives one reply packet, in which case it exits with a success status. The if will then execute the appropriate statement.
Here's my one-liner solution:
screen -S internet-check -d -m -- bash -c 'while ! ping -c 1 google.com; do echo -; done; echo Google responding to ping | mail -s internet-back my-email#example.com'
This runs an infinite ping in a new screen session until there is a response, at which point it sends an e-mail to my-email#example.com. Useful in the age of e-mail sent to phones.
(You might want to check that mail is configured correctly by just running echo test | mail -s test my-email#example.com first. Of course you can do whatever you want from done; onwards, sound a bell, start a web browser, use your imagination.)
I liked paxdiablo's script, but wanted a version that ran indefinitely. This version runs ping until a connection is established and then prints a message saying so.
echo "Testing..."
PING_CMD="ping -t 3 -c 1 google.com > /dev/null 2>&1"
eval $PING_CMD
if [[ $? -eq 0 ]]; then
echo "Already connected."
else
echo -n "Waiting for connection..."
while true; do
eval $PING_CMD
if [[ $? -eq 0 ]]; then
echo
echo Connected.
break
else
sleep 0.5
echo -n .
fi
done
fi
I also have a Gist of this script which I'll update with fixes and improvements as needed.