M BUZZ CRAZE NEWS
// general

Bash: grep including newline

By Emma Martinez

Trying to regex everything between two braces { }. Similar problem toRegex to match any character including newline - Vi & Vim, but I don't understand the solution provided.


Using:

nft list table inet filter

Returns this:

table inet filter { set ipaddr { type ipv4_addr flags timeout elements = { 192.168.1.102, 192.168.1.119, 192.168.1.133, 192.168.1.134 } }
}

I would like to use regex to get everything inside the elements such as:

192.168.1.102,192.168.1.119,192.168.1.133,192.168.1.134 

I thought I could use grep, but whatever works will be great.

4 Answers

The information in the link is specific to vi/vim regex and is not directly applicable to grep.

Probably the equivalent in grep is to use perl compatible regular expressions (PCRE), with the s modifier that tells the regex engine to include newline in the . "any character" set (normally, . means any character except newline).

So for example with pcregrep using the Multiline flag:

$ pcregrep -Mo '(?s)elements = {.*?}' yourexample
elements = { 192.168.1.102, 192.168.1.119, 192.168.1.133, 192.168.1.134 }

You can force something similar in regular GNU grep by using the -z flag as a proxy for -M:

grep -zPo '(?s)elements = {.*?}'

If you want to format the matched text as well, I'd switch to perl proper ex.

$ perl -00nE 'say $1 =~ s/\n\s*/ /r if m/elements = {(.*?)}/s' yourdata 192.168.1.102, 192.168.1.119, 192.168.1.133, 192.168.1.134

Another solution would be:

nft list table inet filter | sed -zne 's/^.*elements = { //; s/ }.*$//; s/[ \n]\+/ /gp'

Here, we are using:

  • the -z option to treat the whole input as a single line.
  • the -n option to suppress automatic printing of pattern space.

In the script part:

  • The first script (s/^.*elements = { //;) deletes the first part starting from beginning and going until elements = { string on input.
  • The second script (s/ }.*$//;) deletes the rest starting from } until the end of input.
  • The third script (s/[ \n]\+/ /gp) replaces any multiple white space (new-line and space characters) with a single space character globally and prints the result.

Here is a sed solution (thanks @FedonKadifeli for the suggestion on shortening my initial command):

nft list table inet filter | sed -zn 's/.*elements = { \([0-9,\. \n]*\) }.*/\1/; s/\n//; s/ \+/ /p'

We pipe the output of nft list table inet filter to a series of sed commands:

  • s/.*elements = { \([0-9,\. \n]*\) }.*/\1/ selects anything between the elements brackets.
  • s/\n// removes any newlines in the selection.
  • s/ \+/ /p replaces multiple space with a single space.

From man sed:

-n, --quiet, --silent suppress automatic printing of pattern space
-z, --null-data separate lines by NUL characters

The output is:

192.168.1.102, 192.168.1.119, 192.168.1.133, 192.168.1.134
0

You can use a bash regex statement.

$ [[ $(nft list table inet filter) =~ \ elements[\ =]+\{([^}]*) ]] && echo "${BASH_REMATCH[1]//[[:space:]]/}"

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy