How to grep a line with unknown number of its indented lines

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
                    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
                    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
                    Frequency:2.437 GHz (Channel 6)
                    Quality=21/70  Signal level=-89 dBm  
                    Encryption key:on
                    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?


The working script is maintained at

Asked By: ceremcem


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.

Answered By: John1024

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


Answered By: vinc17

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
Answered By: roaima

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="nn";IGNORECASE=1} /wireless/'

and pcregrep in second:

iwlist wlan0 scan | pcregrep -iMo 'cell 13(.|n)*(?=cell 14)'
Answered By: jimmij

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}'

Answered By: Costas

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

        set     '[:blank:]' "$1" '
';      sed -n " /^(.*n)*[$1]*$2/,$!{N
        };       /./!p;//!d;H;x

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
                    Frequency:2.437 GHz (Channel 6)
                    Quality=21/70  Signal level=-89 dBm  
                    Encryption key:on
          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.

Answered By: mikeserv

Use blockgrep


blockgrep [OPTIONS] PATTERN [FILE...]

Searches for PATTERN in each FILE (or STDIN, if no filenames are supplied).

Unlike other greps, blockgrep outputs blocks (typically of code) which contain a match.

Answered By: mfg
Categories: Answers Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.