SIddharth M.
SIddharth M.

Reputation: 29

Finding string in between brackets in bash

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

Answers (4)

Walter A
Walter A

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

Ljm Dullaart
Ljm Dullaart

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

Charles Duffy
Charles Duffy

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

iamauser
iamauser

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

Related Questions