Reputation: 192
Here's a short version of my situation. My current ISP is not working properly, and as a result, I'd like to log ping successes and failures as proof it isn't working. I'll be on Linux, but Windows would be fine too. I've been reading, and as far as I can tell, a shell script or Python code would work best.
So, being more detailed: I'd like to do a single ping at a website every 5 seconds. If there is a successful ping, it should output the normal results of the ping to a file. Ping failures however have a long timeout, so I'd like to make the program so that if the ping hasn't succeeded in 4 seconds or so, it stops the ping attempt, but still outputs the full ping data with a "1 packet transmitted, 0 received" etc, like it would have if it had run out of time.
Here is what I have so far:
while true
do
ping -c1 -w4 www.example.com >> log.txt
sleep 5
done
Unfortunately, there are 2 problems. First, the "-w4" isn't making it time out after 4 seconds like I thought it would. And second, the "sleep" adds on to the time already spent. So, if the ping takes 3 seconds, the loop will take 8 seconds. Since I will be writing another program that will count the successes, failures, and output the down time percent, having loops take a lot of extra time on a failure isn't acceptable.
So, given what I need, what's the best way to go about this? Can what I have be improved to meed the needs? Thank you for any help!
PS: I know almost nothing about writing shell scripts, (though I'm competent with a terminal), and I know even less about Python (though I know C++). Apologies in advance!
Upvotes: 1
Views: 4393
Reputation: 365707
If you really want to make the whole loop take no more than 5 seconds, you have to know how long ping took, then subtract that from 5, and sleep the difference.
You can do this from bash either by recording the time before and after ping and subtracting, or by using the time -p
command to run the ping
(the second column of the first non-blank line will be wall-clock time as a float, like 0.01
).
But I think this will be easier from Python:
import datetime
import subprocess
import time
while True:
start = datetime.datetime.now()
with open('log.txt', 'a+b') as logfile:
subprocess.call(['ping', '-c1', '-w4', 'www.example.com'], stdout=logfile)
delta = datetime.datetime.now() - start
time.sleep(delta.total_seconds())
Of course this won't work if ping
doesn't finish in at most 5 seconds, because you can't sleep for negative time. You could solve that by using an explicit Popen
and calling wait(5)
on it, then killing if it fails…
But it's a lot simpler to just always give it 5 seconds, whether it needs that much or not:
while True:
with open('log.txt', 'a+b') as logfile:
ping = subprocess.Popen(['ping', '-c1', '-w4', 'www.example.com'],
stdout=logfile)
time.sleep(5)
# Not strictly necessary, but it avoids signaling for no reason
if ping.poll() is None:
ping.kill()
ping.wait()
Upvotes: 1
Reputation: 75488
You can put ping on the background instead and use another subprocess to wait and kill it:
while true
do
ping -c1 www.example.com >> log.txt &
p=$!
( sleep 4s; kill -s SIGABRT "$p"; ) &>/dev/null &
wait "$p"
sleep 5
done
Adjust the timeout if needed.
Upvotes: 0