Ping inside FOR loop

Hello,

I'm trying to write (my own primitive) traceroute command that would write out all IPs that packets are hoping through. :slight_smile:

So I've put the ping command inside a for loop

#!/bin/bash
domain=${1-www.google.com}; #If no argument, then do www.google.com.

for ((i=1; i<=30; i++)) # max. 30 hops
do 
	ping -t $i $domain # I would later add "grep" to extract IPs
done

exit 0;

What is wrong with this script? All I get is 30 "Usage: ping [...]" replies. :confused:

EDIT: I see it: I've written $1 instead of $i. :o But don't you worry, I will most surely come back for help with grep! :rolleyes:

Hi.

I think that should be $i, not $1 in your ping command. But -t is used to set a ping timeout.

According to the man page on my system -R is used to record the route. You might also want to use -c?

1 Like

First, thank you for the reply.

  1. I've seen other scripts that used, say, -c 2, but I don't (really) understand why (yes, I looked at man ping).

    In ICMP's first 8 bits I'll be getting #11 (TTL expired) and not #0 (ECHO_REPLY), which is what switch -c is referring to. Well, what is wrong with my "logic"?
    ADD: Should I use -c 1 for each hop on the way, is that right?
  2. How to find the right maximum TTL number? :confused: for example, pinging google.com it takes 14 hops. So, if I do the for loop 30 times, the last 16 IPs are all (almost) the same.

You miss a colon :

domain=${1:-www.google.com}

By the way you should check the ping option available for your OS version
(look at the -c option that let you ping a <count> times so the ping isn't "endless")

Hi ctsgnb,
the "default option" works also without the colon. :wink:

I'd be most grateful for any help on 2. question (on choosing max. TTL) ...
and of course any correction on my "thinking" in 1. point (on -c switch). :o

ADD: I think I should let ping run inside the for loop, until I'd get ECHO_REPLY, and then break the for loop.

PING www.l.google.com (209.85.135.99) 56(84) bytes of data.
From 209.85.253.26 icmp_seq=1 Time to live exceeded

--- www.l.google.com ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms
Here should be the break-like statement: above is TTL_EXPIRED, below ECHO_REPLY. Is that right?
PING www.l.google.com (209.85.135.99) 56(84) bytes of data.
64 bytes from mu-in-f99.1e100.net (209.85.135.99): icmp_req=1 ttl=250 time=45.0 ms

--- www.l.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 45.052/45.052/45.052/0.000 ms

The colon isn't really a requirement in this case:

/ $ set -- www.bbc.co.uk    
               
/ $ domain=${1-www.google.com}
/ $ echo $domain
www.bbc.co.uk

/ $ domain=${2-www.google.com}
/ $ echo $domain              
www.google.com

O/P: -c has nothing to do with hops, but how many times you want to attempt a ping before stopping.

There is no "right TTL" number. Quote from the ping man page:

I'll sleep a little bit less ignorant tonight :smiley:

So, for emulating traceroute it should be -c 1, right?

Can you help me with my 2nd question: how could I "break" from the for loop when above quoted change (from TTL_EXPIRED to ECHO_REPLY) occurred? Perhaps with a help from grep? How? :o

You could (probably!) say:

ping .... | grep ECHO_REPLY && break

But I'm not really understanding why you have a for-loop in the first place to ping the same thing 30 times...

1 Like

There isn't actual word "ECHO_REPLY" but a line that starts with "64 bytes from [...]" when the script reaches the target computer (eg. google.com). Now, how do you write an if statement like:

if (line starts with "64 bytes from [...]")
do
    break;
fi

:confused:

You're right, I should use while or until, but I don't know how to express the terminating if statement.
But I'm not pinging "the same thing", because I increase TTL in each iteration and get new servers where the packet "expires".

This is (almost) it:

#!/bin/bash
domain=${1-www.google.com}; #If no argument, then do www.google.com. 

for ((i=1; i<=30; i++))
do 
	ping -c 1 -t $i $domain | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'
done

exit 0;

Above script outputs this:

209.85.135.147 # target: google.com
192.168.1.1      # default gateway
209.85.135.147 # wrong
85.10.0.254
209.85.135.105 # wrong
95.176.241.10
209.85.135.106 # wrong
209.85.135.106 # wrong: twice in a row because the 4th hop returns all stars (* * *) as in built-in traceroute
85.10.0.73
209.85.135.105 # wrong
85.10.0.74
209.85.135.103 # wrong
212.18.32.97
209.85.135.99 # wrong
212.18.39.214
209.85.135.147 # target reached: all below are unnecessary PINGs
72.14.219.148
209.85.135.99 
etc.

EDIT: Wait, I'm doing it wrong: I also pickup google's IP from each line "PING www.l.google.com (209.85.135.103) 56(84) bytes of data.". How do I exclude these IPs with grep?