Search code examples
sedgrep

Linux shell show [section] with grep command


I need to parse multiple files from asterisk to get informations that refer to same objects but are stored in different positions (ideally I would like to get the sectionname reference).

Now I look for a specific string with:

    grep -OPTIONS /DIRECTORY -e SEARCHSTRING

OPTIONS
       -r, --recursive
              Read all files under each directory, recursively, following symbolic links only if they are on the command line.  This is equivalent to the -d recurse option.

       -n, --line-number
              Prefix each line of output with the 1-based line number within its input file.  (-n is specified by POSIX.)

i.e. seach string "outrt-5" in folder /etc/asterisk:

# grep -rn /etc/asterisk/ -e  outrt-5 
/etc/asterisk/extensions_additional.conf:4462:include => outrt-5 ; test
/etc/asterisk/extensions_additional.conf:4662:[outrt-5] ; test
/etc/asterisk/extensions_additional.conf:4663:include => outrt-5-custom
/etc/asterisk/extensions_additional.conf:4677:;--== end of [outrt-5] ==--;

Now I've retrieved the single line number reference and filenames.

How can I retrieve all the rows in the section or at least the nameofsection (a section is delimited by [nameofsection] )?

Is sed - stream editor for filtering and transforming text the best option?

# STARTROW=4460
# NEXTROW=4462
# ENDROW=4462

sed -n "${STARTROW}, ${NEXTROW} p; ${ENDROW} q" /etc/asterisk/extensions_additional.conf 

i.e. /etc/asterisk/extensions_additional.conf section near line:4462:

[outbound-allroutes]
include => outbound-allroutes-custom
include => outrt-5 ; test

So now you can see that outrt-5 is inside outbound-allroutes

Is there a way to get as search result:

> /etc/asterisk/extensions_additional.conf:4462:include => outrt-5 ;[outbound-allroutes]

?


Solution

  • I generally use sed for simple block selection scripts. For example -

    $: cat file # note that there is NO blank line at the end
    [outbound-allroutes]
    include => outbound-allroutes-custom
    include => outrt-1 ; test first block
    
    [outbound-allroutes]
    include => outbound-allroutes-custom
    include => nope-5 ; test
    
    [outbound-allroutes]
    include => outbound-allroutes-custom
    include => outrt-2 ; test embedded block
    
    [outbound-allroutes]
    include => outbound-allroutes-custom
    include => nope-5 ; test
    
    [outbound-allroutes]
    include => outbound-allroutes-custom
    include => nope-5 ; test
    
    [outbound-allroutes]
    include => outbound-allroutes-custom
    include => outrt-3 ; test last/END block
    

    I'm going to grab every block that has outrt. You can modify the logic a bit to fit your exact needs.

    $: cat tst
    sed -n '
     /\[.*\]/,/^[[:space:]]*$/{
       /\[.*\]/{h; n;}
       /^[[:space:]]*$/{ H; x; /outrt/{p; n;} }
       H;
     }
     ${ x; /outrt/p }
    ' file
    
    $: ./tst
    [outbound-allroutes]
    include => outbound-allroutes-custom
    include => outrt-1 ; test first block
    
    [outbound-allroutes]
    include => outbound-allroutes-custom
    include => outrt-2 ; test embedded block
    
    [outbound-allroutes]
    include => outbound-allroutes-custom
    include => outrt-3 ; test last/END block
    

    This is GNU sed. If you don't have that we'll need to make some modifications.

    grep is good, but it doesn't really allow complex logic that defines arbitrary length multiline data.

    All that being said, as William Pursell pointed out, awk can make this sort of thing trivial if you know how. Using the same input file -

    $: awk -v RS= '/outrt/{print $0 "\n"}' file
    [outbound-allroutes]
    include => outbound-allroutes-custom
    include => outrt-1 ; test first block
    
    [outbound-allroutes]
    include => outbound-allroutes-custom
    include => outrt-2 ; test embedded block
    
    [outbound-allroutes]
    include => outbound-allroutes-custom
    include => outrt-3 ; test last/END block