Reputation: 29
Following is my code:
#!/bin/bash
echo "Enter a website name here : "
read name #reads the user input
echo "Website name entered : $name"
echo "$name"
traceroute -I $name
read -t lines < <(traceroute -I $name)
N=0 # loop to convert the output of "traceroute" into array
for i in $(traceroute -I $name);
do
myarray[$N]="$i"
echo "$N = $i"
let "N= $N + 1"
done
echo "${#myarray[@]}" #echoes length of array
echo "The connections made include's the following IP Addresses : "
for ((i=2; i < ${#myarray[@]} ; i=$i+9));
do
echo $str | cut -d "(" -f5 | cut -d ")" -f1
echo ${myarray[${i}]}
done
I have been trying to use the last loop in the bash script to echo output of text in between brackets of the traceroute command. For example, if the traceroute output is:
ip4.gtt.net (173.205.40.26) 132.149 ms 129.899 ms 129.892 ms
I want the loop to only echo text in between the brackets. Therefore, 173.205.40.26
I am not sure what to use. I have been looking at regular expressions in bash but didn't find anything too useful.
Upvotes: 1
Views: 476
Reputation: 19982
Like @LjmDullaart, I found different solutions too.
The cut
command is straight forward, I had the same solution:
echo "${str}" | cut -d "(" -f2 | cut -d ")" -f1
For sed
my solution (remember the part between the ()
and recall it with \1
) gets a little confusing: ()
is also used by sed
.
echo "${str}" | sed 's/.*(\(.*\)).*/\1/'
# or with the -r flag
echo "${str}" | sed -r 's/.*\((.*)\).*/\1/'
A third way is looking for an IP-address
echo "${str}" | grep -Eo "[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}"
EDIT:
A dot is a wildcard, so it is better to use
grep -Eo "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
The first form finds 129.899
. This string matches [0-9]{1}.[0-9]{1}.[0-9]{1}.[0-9]{1}
.
EDIT 2:
Less ambiguous (Tx @CharlesDuffy for the comment) is
grep -Eo "[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}"
Upvotes: 1
Reputation: 4969
This puzzle has multiple answers. First one is what you did yourself, only use -f2
instead of -f5
:
str='ip4.gtt.net (173.205.40.26) 132.149 ms 129.899 ms 129.892 ms'
echo "$str" | cut -d "(" -f2| cut -d ")" -f1
173.205.40.26
Or using sed
:
echo "$str" | sed 's/.*(//;s/).*//'
sed
here has two parts, separated by the ;
. The first part substitutes all characters .*
and the opening parentheses (
by an empty string, resulting in 173.205.40.26) 132.149 ms 129.899 ms 129.892 ms
; the second part replaces the closing parentheses )
and all characters that follow .*
with an empty string, resulting in 173.205.40.26
.
Or completely in bash
:
str2=${str//*(/}
str3=${str2//)*/}
echo $str3
Upvotes: 1
Reputation: 295298
If you really want to use a regex, use bash's built-in support:
# our regular expression, in POSIX ERE form
re='[(](.*)[)]'
# our string to test
str='ip4.gtt.net (173.205.40.26) 132.149 ms 129.899 ms 129.892 ms'
# if the string matches the regex, print the first (only) match group
[[ $str =~ $re ]] && echo "${BASH_REMATCH[1]}"
properly emits:
173.205.40.26
To do this properly in a BashFAQ #1 loop would look like:
re='[(](.*)[)]'
while IFS= read -r str; do
[[ $str =~ $re ]] && echo "${BASH_REMATCH[1]}"
done < <(traceroute -I "$name")
Upvotes: 0
Reputation: 11469
Use negative lookback
in GNU grep
.
$ str='ip4.gtt.net (173.205.40.26) 132.149 ms 129.899 ms 129.892 ms'
$ grep -o -P '(?<=\().*(?=\))' <<<"${str}"
173.205.40.26
To match exactly an IPv4 IP,
grep -o -P '(?<=\()[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}(?=\))' <<<"${str}"
Here is an example of getting the entire stack of IPs in the connection.
#!/bin/bash
traceroute -I example.com > connFile
grep -o -P '(?<=\()[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}(?=\))' connFile
Upvotes: 0