6

I often need to grep a line with its indented sections. For example, if the indented line number may be known, we would achieve this goal with the following:

$ lspci -vq | grep -i wireless -B 1 -A 5

    02:00.0 Network controller: Intel Corporation Wireless 7260 (rev 73)
        Subsystem: Intel Corporation Wireless-N 7260
        Flags: bus master, fast devsel, latency 0, IRQ 64
        Memory at c0600000 (64-bit, non-prefetchable) [size=8K]
        Capabilities: <access denied>

For example, the following command's output may or may not contain all of only one wireless lan information:

$ sudo iwlist wlan0 scan | grep -i "cell 13" -A 34
          Cell 13 - Address: 00:1A:2B:93:A7:9C
                    Channel:6
                    Frequency:2.437 GHz (Channel 6)
                    Quality=20/70  Signal level=-90 dBm  
                    Encryption key:on
                    ESSID:"NetMASTER Uydunet-E445"
                    Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s; 18 Mb/s
                              24 Mb/s; 36 Mb/s; 54 Mb/s
                    Bit Rates:6 Mb/s; 9 Mb/s; 12 Mb/s; 48 Mb/s
                    Mode:Master
                    Extra:tsf=0000005b923c8227
                    Extra: Last beacon: 4648ms ago
                    IE: Unknown: 00164E65744D415354455220557964756E65742D45343435
                    IE: Unknown: 010882848B962430486C
                    IE: Unknown: 030106
                    IE: Unknown: 050400010000
                    IE: Unknown: 2A0100
                    IE: Unknown: 2F0100
                    IE: Unknown: 32040C121860
                    IE: Unknown: 2D1A2C181BFF00000000000000000000000000000000000000000000
                    IE: Unknown: 3D1606080400000000000000000000000000000000000000
                    IE: Unknown: DD090010180201F00C0000
                    IE: WPA Version 1
                        Group Cipher : CCMP
                        Pairwise Ciphers (1) : CCMP
                        Authentication Suites (1) : PSK
                    IE: Unknown: DD180050F2020101800003A4000027A4000042435E0062322F00
          Cell 14 - Address: 90:F6:52:90:C2:2F
                    Channel:6
                    Frequency:2.437 GHz (Channel 6)
                    Quality=21/70  Signal level=-89 dBm  
                    Encryption key:on
                    ESSID:"selma"
                    Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s; 6 Mb/s
                              9 Mb/s; 12 Mb/s; 18 Mb/s

How can I make this output cropped to contain only one Cell information?

Edit

The working script is maintained at https://github.com/ceremcem/sgrep

6

Solution based on matching section headings

To print the text starting with Cell 13 but stopping before Cell 14, use:

sudo iwlist wlan0 scan | awk '/Cell 13/{f=1} /^ *Cell 14/{f=0} f'

Solution based on monitoring the indent level

This will print all lines starting with the one that contains Cell 13 and continuing with all the lines that follow that have a greater indent level:

sudo iwlist wlan0 scan | awk '/Cell 13/ && !f{f=1;x=$0;sub(/[^ ].*/,"",x);x=x" ";print;next} f {if (substr($0,1,length(x))==x)print; else f=0}'

How it works:

The code use two variables. f is a flag which is 1 if we are in the section that we want to print and 0 otherwise. The string x is set to one space greater than the indent at the start of the section that we want to print.

  • /Cell 13/ && !f{f=1;x=$0;sub(/[^ ].*/,"",x);x=x" ";print;next}

    This looks for lines that contain Cell 13 (or whatever would signify the start of the section. If a line matches that and if we are not already in that section, then:

    1. f is set to 1.

    2. The string x is set to the indentation that begins the line.

    3. This line is printed.

    4. The remaining commands are skipped and we jump to the next line.

  • f {if (substr($0,1,length(x))==x)print; else f=0}

    If we are within the section to be printed (f is non-zero), then check the indentation level. If the indentation is at least x, then print this line. Otherwise, signal that we have reached the end of the section by setting f=0.

4
  • Your second command may work on the OP's example, but not if a line of greater indent level matches the regexp, like in my example.
    – vinc17
    Feb 12 '15 at 0:21
  • @vinc17 OK. Answer updated to handle that case too.
    – John1024
    Feb 12 '15 at 0:32
  • Your solution works as expected and seems more helpful my further improvements (such as "match with child section's line"). But I think I should accept vinc17's answer, since he posted the first working solution. Thank you for your help.
    – ceremcem
    Feb 12 '15 at 0:53
  • A problem is that it fails to find a match if the line comes immediately after the end of a previous section. To solve this replace "f=0" with the following: {if ($0 ~ /Cell 13/) {f=1;x=$0;sub(/[^ ].*/,"",x);x=x" ";print} else f=0}. Yes I know it is a bit ugly Oct 13 '19 at 19:43
1

If you want to output a REGEXP match and all the indented lines that are just after the match:

command | perl -ne '/^( *)/; $i = length $1; $j && $i >= $j and print, next; $j = 0; /REGEXP/ and $j = $i + 1, print'

replacing REGEXP by your regular expression.

For instance,

$ perl -ne '/^( *)/; $i = length $1; $j && $i >= $j and print, next; $j = 0; /a/ and $j = $i + 1, print' <<EOF
a
b
  c
  a
    b
    a
    c
      b
    d
  e
    b
  a
    c
e
EOF

outputs:

a
  a
    b
    a
    c
      b
    d
  a
    c
1
  • Your code didn't work at first (it was my fault) so I accepted the other answer before. Thank you.
    – ceremcem
    Feb 12 '15 at 0:51
1

The general solution is to use sed with range address. All of you need somehow indicate the lines where to start and to stop. It can be keyword(s), indent level, empty line or something else:

  • keyword

    iwlist wlan0 scan | sed -n '/Cell 04/{:1;p;n;/Cell/!b1}'

  • indent level

    iwlist wlan0 scan | sed -n '/Cell 04/{:1;p;n;/^\s\{1,18}\S/!b1}'

  • empty line

    lspci -vq | sed -n '/[Ww]ireless/{:1;p;n;/^$/!b1}'

1

Here is an example of how this might be done w/ sed:

srch_rng(){                      
        set     '[:blank:]' "$1" '\
';      sed -n " /^\(.*\n\)*[$1]*$2/,\$!{N
                s/^\(\([$1]*\)[$1]*[^$1].*\n\)\(\2[^$1]\)/\3/
                        s/^/$3/;D           
        };       /./!p;//!d;H;x
                 /^\n[$1]*/!h;s///p;t       
                s/\([$1]*\).*\n\1//;/^[$1]/p
                 //!q;x;s/\n.*//;h"
}

This works both sides of a range - until sed matches the pattern given as its first argument aginst its stdin it will retain in memory as far back as each top-level indent it encounters. After matching its first argument, it stores nothing but the indent.

Before it makes the match it works in a cycle of pulling in the Next line, attempting to s///ubstitute away all of pattern space if the last line pulled in opens with less than or equal to the same amount of white space as the first line in its buffer followed immediately by a non-blank character, then looping back to find out if it made a match for the most recent line or if it should try again.

Once it makes the match it first prints all it has retained so far, deletes it, and on the next cycle and from there deletes all but the trailing white space. For the rest of its runtime it continues to print each line as it reads it until it encounters another line that opens with less than or equal to the indent level it has stored - at which time it quits input entirely.

In this way it keeps its buffers as current (and as empty) as it can and does not bog down unless there are very long stretches between top-level indents before it can match the pattern. It also does not care for intervening blank lines - the printed information can span paragraph boundaries but will not span top-level indents.

So, for example, if I just do srch_rng Cell it will print from the Cell 13 line up to the next Cell. It will also do this if I specify ESSID as the first argument, but if I do:

srch_rng '.*selma'

...it prints...

Cell 14 - Address: 90:F6:52:90:C2:2F
                    Channel:6
                    Frequency:2.437 GHz (Channel 6)
                    Quality=21/70  Signal level=-89 dBm  
                    Encryption key:on
                    ESSID:"selma"
          Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s; 6 Mb/s
                    9 Mb/s; 12 Mb/s; 18 Mb/s%                

The .* is needed because it defaults to matching only the first non-blank characters on a line. And, as you can see, for every line it prints it strips only the leading whitespace it has stored in the buffer from each - which puts the first line printed at the far left and all others evenly indented beneath.

0

This will match from Cell 13 to the next occurrence of Cell

sudo iwlist wlan0 scan | awk '/Cell/{p=0} /Cell 13/{p=1} p'

There are three commands in the awk statement:

  1. /Cell/ {p=0} - if the word "Cell" exists in the line, stop printing (if we were printing previously
  2. /Cell 13/ {p=1} - if the string "Cell 13" exists in the line, start printing
  3. p - print the entire line (that's implied because of no {...} part) if p != 0
2
  • Thank you for your help, but your solution is specific to iwlist wlan0 scan command's output. I need more general solution. Thank you though.
    – ceremcem
    Feb 12 '15 at 0:57
  • @Ceremcem it is a specific instance of a general solution. You need to provide a starting match and an ending match and substitute them into the awk. This substitution could be done with variables.
    – roaima
    Feb 12 '15 at 7:36
0

The command grep processes text in lines, whole lines. There are some options which can force it to match only part of the line, or (as in your example) span pattern to match more than one line, but usually if you need to do use such options it means that other tools should be used instead.

That being said I would rather use awk in your first example:

lspci -vq | awk 'BEGIN{RS="\n\n";IGNORECASE=1} /wireless/'

and pcregrep in second:

iwlist wlan0 scan | pcregrep -iMo 'cell 13(.|\n)*(?=cell 14)'
1
  • Thank you for your help, but your solution is specific to iwlist wlan0 scan command's output. I need more general solution. Thank you though.
    – ceremcem
    Feb 12 '15 at 0:56

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.