GREP for next word follow a string containing a variable

Viewed 101

I have this string in a bash variable

name : br-ext protocols : [protocol] name : br-ex protocols : [] name : br-local protocols : [other protocol] name : br-int protocols : []

and a variable $br containing on of the name : values (i.e br-ext)

Given $br, I want to access the protocol string that follows it (so $br br-ext should get 'protocol' and $br-ex should get '')

I've tried this, but it doesn't seem to access the value of the variable

echo $protocols | grep -oP "(?<=\"$br\")[^ ]*"

Any tips?

4 Answers

You can use

echo "$protocols" | grep -oP "$br\\s+protocols\\s*:\\s*\\[\\K[^][]*"

See the online demo

Note that $protocols is inside double quotes (the variable is quoted).

The pattern matches

  • \s+ - one or more whitespaces
  • protocols - the word protocols
  • \s*:\s* - a colon enclosed with zero or more whitespaces
  • \[ - a [ char
  • \K - remove all text matched so far
  • [^][]* - zero or more chars other than [ and ].

With your shown samples, please try following awk code, where protocol and br are shell variables.

echo "$protocols" | 
awk -v br="$br" '
{
  match($0,br" [^:]*:[[:space:]]+[^]]*")
  val=substr($0,RSTART,RLENGTH)
  sub(/.*\[/,"",val)
  print val
}
'

Explanation: Adding detailed explanation for above code.

echo "$protocols" |               ##Printing value of protocols variable and sending it as standard input to awk program here.
awk -v br="$br" '                 ##Starting awk program from here, defining br variable of awk which has br shell variable value in it.
{
  match($0,br" [^:]*:[[:space:]]+[^]]*") ##using match function of awk to match br value followed by space till colon 1st occurrence followed by spaces till 1st occurrence of ].
  val=substr($0,RSTART,RLENGTH)   ##Creating val which has matched regex sub string value here.
  sub(/.*\[/,"",val)              ##Using sub to substitute everything till [ in val.
  print val                       ##printing val here.
}
'

Assuming br can never contain regexp metachars or /s then using any sed in any shell on every Unix box:

$ str='name : br-ext protocols : [protocol] name : br-ex protocols : [] name : br-local protocols : [other protocol] name : br-int protocols : []'

$ br='br-ext'
$ echo "$str" | sed -n 's/.*name : '"$br"' protocols : \[\([^]]*\).*/\1/p'
protocol

$ br='br-ex'
$ echo "$str" | sed -n 's/.*name : '"$br"' protocols : \[\([^]]*\).*/\1/p'

$

You can do this in pure Bash, without using external utilities. (But the solution is definitely bash-specific. Other similar features exist in other shells, but there's not standardization.)

[[ $protocol =~ "name : $br protocols : ["([^]]*)"]" ]] \
  && extracted=${BASH_REMATCH[1]} \
  || echo "Not found"

The above pattern assumes the input is exactly as shown in your question. Some adjustment might be necessary if that assumption is wrong.

  • =~ is the regex match operator used in Bash's extended conditional expression ([[...]]) syntax
  • $BASH_REMATCH is where =~ leaves the captured strings if the pattern has captures.
  • As in most regex implementations, [^]] means "anything other than ] because the first character in a character set is allowed to be ] or -.
  • Note: regex metacharacters in regular expressions can be tricky because the part of the regular expression in quotes is treated as an ordinary string, regardless of its comments. So I couldn't just write the whole pattern in quotes.
Related