Looking for an awk command to print strings only if substring is missing

I have a file that I need to find each interface that has move-group on the interface line and print this line if the lines under the interface does Not have "filter-shared 14".

Example file:

interface 1/1/1/0 move-group
     decription one one one zero
     no shut
     filter-shared 14
     accounting-policy 5
     shared-address 0
interface 1/1/1/1 move-group
     decription one one one one
     no shut
     accounting-policy 5
     shared-address 0
interface 1/1/1/2 move-group
     decription one one one two
     no shut
     filter-shared 14
     accounting-policy 5
     shared-address 0
interface 1/1/1/3 move-group
     decription one one one three
     no shut
     accounting-policy 5
     shared-address 0
interface 1/1/1/4 permanent-group
     decription one one one four
     no shut
     accounting-policy 5
     shared-address 0

I only wish to print out the interface line that does have "filter-shared 14" under it.
So the output would look like this:

interface 1/1/1/1 move-group
interface 1/1/1/3 move-group

Does it have to be AWK? Try this if not:

perl -ln0e 'while (/(interface[^\n]+).*?(?=interface|$)/sg){$x=$&;$y=$1;print $y if $x!~/filter-shared 14/ && $y=~/move-group/}' file
1 Like

Stash each interface line in a variable a, and 0 in another b, and as your seaarch forward, look for filter-shared 14, and if you find it, st the second var b to 1, and at the next interface line or EOF if b still a zero, print the stash a before stashing the new interface line. So, in code the last bit comes first!

bartus11, That is a perfect perl line, but the server I need to run this on does not have perl.

One way with awk:

awk '/^interface/{print x}1' file |
awk '!/filter-shared 14/ && $1~/move-group/{print $1}' FS='\n' RS=
1 Like

Thank you Scrutinizer, that works. But it skips the first line of the file if there is a match. I think it's because x is empty until after the first line.

Hi Numele, I cannot reproduce this skipping of the first line. Could you give an example and could you specify on what OS you are getting this result?

I get:

interface 1/1/1/1 move-group
interface 1/1/1/3 move-group

If I change "filter-shared 14" of the first record to something else, then the output becomes:

interface 1/1/1/0 move-group
interface 1/1/1/1 move-group
interface 1/1/1/3 move-group

Note that there are two parts. The first puts empty lines around the records, the "x" is an empty variable, so that an empty line gets printed before that start of each record.

This then enables the second awk to use RS= , a special value so that records can be separated by blank lines and thus become multi-line records. So for example the first record becomes:

interface 1/1/1/0 move-group
     decription one one one zero
     no shut
     filter-shared 14
     accounting-policy 5
     shared-address 0

Because the fields separator FS is set to \n , the first field ( $1 ) is equal to the first line of such a multiline record..

awk '$0 !~ /filter-shared 14/ && $1 ~ /move-group/ {print (RS $1)}' FS='\n' RS='interface'  file

Note: Using a regular expression instead of a single character with RS is an extension supported only by gawk and mawk ..

Scrutinizer,
Your awk line works very well, thank you. I was using a test file for it first but when using it on the actual files, it worked out fine.
Thanks again!

Hi.

This longish post is intended to show a general technique for working with paragraphs or stanzas of data. Each stanza is transformed so that a copy of it becomes a single, standard text line, embedded newlines are converted to some other character (in the specific case "|"). This creates an intermediate structure that one could call a super-line.

The super-lines are then manipulated to obtain a final set of super-lines that conform to the requirements. Finally, the super-lines are split back to normal, and final filtering can be done.

This script performs that combination of operations. The creation of super-lines can be done with awk, perl, etc. I used a perl utility from github called cat0par (see script for link). The script also uses a few local tools such pll, specimen, etc.

#!/usr/bin/env bash

# @(#) s1	Demonstrate join / split of super-line to search embedded string.
# See:
# https://github.com/jakobi/script-archive/blob/master/cli.list.grep/cat0par
# or:
# https://raw.githubusercontent.com/jakobi/script-archive/master/cli.list.grep/cat0par

# Utility functions: print-as-echo, print-line-with-visual-space, debug.
# export PATH="/usr/local/bin:/usr/bin:/bin"
LC_ALL=C ; LANG=C ; export LC_ALL LANG
pe() { for _i;do printf "%s" "$_i";done; printf "\n"; }
pl() { pe;pe "-----" ;pe "$*"; }
db() { ( printf " db, ";for _i;do printf "%s" "$_i";done;printf "\n" ) >&2 ; }
db() { : ; }
pe
what $( which pll cat0par )
C=$HOME/bin/context && [ -f $C ] && $C specimen cat0par grep sed pll

FILE=${1-data1}

pl " Sample input data file $FILE:"
specimen 5 $FILE

# Collect and join all lines in an "interface" stanza into a super-line.
# Select "move-group: super-lines.
# Omit "filter-shared 14" super-lines.
# Split super-lines back into stanzas.
# Select "interface" lines.

rm -f f[1-9]
pl " Results:"
cat0par -nl -nonl='|' -start "interface" $FILE |
tee f1 |
grep "move-group" |
tee f2 |
grep -v "filter-shared 14" |
tee f3 |
sed 's/|/\n/'g |
tee f4 |
grep "interface"

pl " Content of intermediate files:"
pll f?

exit 0

producing:

$ ./s1

pll	Print long-line, section, trim, shorten to width as necessary.
cat0par	Combine/split lines to stanza/groups/superlines.

Environment: LC_ALL = C, LANG = C
(Versions displayed with local utility "version")
OS, ker|rel, machine: Linux, 2.6.26-2-amd64, x86_64
Distribution        : Debian 5.0.8 (lenny, workstation) 
bash GNU bash 3.2.39
specimen (local) 1.17
cat0par (local) 1.3
grep GNU grep 2.5.3
sed GNU sed version 4.1.5
pll (local) 1.22

-----
 Sample input data file data1:
Edges: 5:0:5 of 27 lines in file "data1"
interface 1/1/1/0 move-group
     decription one one one zero
     no shut
     filter-shared 14
     accounting-policy 5
   ---
interface 1/1/1/4 permanent-group
     decription one one one four
     no shut
     accounting-policy 5
     shared-address 0

-----
 Results:
interface 1/1/1/1 move-group
interface 1/1/1/3 move-group

-----
 Content of intermediate files:
 (Longest line: 143; fit into lines of length 78)
         1         2         3     ...     11        12        13        
12345678901234567890123456789012345...78901234567890123456789012345678901
FILE = f1
interface 1/1/1/0 move-group|     d...unting-policy 5|     shared-address
interface 1/1/1/1 move-group|     d...unting-policy 5|     shared-address
interface 1/1/1/2 move-group|     d...unting-policy 5|     shared-address
interface 1/1/1/3 move-group|     d...unting-policy 5|     shared-address
interface 1/1/1/4 permanent-group| ...unting-policy 5|     shared-address

FILE = f2
interface 1/1/1/0 move-group|     d...unting-policy 5|     shared-address
interface 1/1/1/1 move-group|     d...unting-policy 5|     shared-address
interface 1/1/1/2 move-group|     d...unting-policy 5|     shared-address
interface 1/1/1/3 move-group|     d...unting-policy 5|     shared-address

FILE = f3
interface 1/1/1/1 move-group|     d...unting-policy 5|     shared-address
interface 1/1/1/3 move-group|     d...unting-policy 5|     shared-address

FILE = f4
interface 1/1/1/1 move-group
     decription one one one one
     no shut
     accounting-policy 5
     shared-address 0
interface 1/1/1/3 move-group
     decription one one one three
     no shut
     accounting-policy 5
     shared-address 0

The intermediate files are displayed to show the progress of the operation. They can be omitted for production work.

Best wishes ... cheers, drl

1 Like

Relatively easy in sed:

sed -n '
    :loop
    N
    /\ninterface/!{
        $!{
            s/\n/:/
            b loop
            }
        s/$/\
/
        }
    /filter-shared 14/!{
        s/:.*\n/\
/
        P
        }
    s/.*\n//
    b loop
' file

Narrative: Set a branch tag 'loop' for looping, add another input line to the buffer, if the final line in the buffer is not an interface line then if it is not EOF then turn the linefeed into a colon and go back to loop else for EOF put a linefeed on the end of the line, then if there is no key string in the buffer, remove all but the interface lines and print the first interface line, discard the first line(s) in the buffer and loop back for more lines.

1 Like

Hi, DGPickett.

Your sed code, sed GNU sed version 4.1.5 , results in:

interface 1/1/1/1 move-group
interface 1/1/1/3 move-group
interface 1/1/1/4 permanent-group

Best wishes ... cheers, drl

One more issue came up, I need to print the interface lines that match and also the next line after the interface.

I have this awk statement, but I can't get the line after, the description line to print.

nawk 'END {if (!ok) print r} /interface/ && /move-group/ {if (!ok) print r; r = $0; ok = x}/filter-shared 14/ {r = r RS $0; ok++}' inputfile
interface 1/1/1/1 move-group
interface 1/1/1/3 move-group

I need it to say

interface 1/1/1/1 move-group
     decription one one one one
 
interface 1/1/1/3 move-group
      description one one one three

Hi.

Changing the final grep in my solution to:

grep -A1 "interface"

produces:

 Results:
interface 1/1/1/1 move-group
     decription one one one one
--
interface 1/1/1/3 move-group
     decription one one one three

Where the removal of the separator line if desired is left as an exercise.

Best wishes ... cheers, drl

Thank you for the input DRL, but I do not have that grep option A1 on my server.

Just also print $2 and an empty line:

awk '/^interface/{print x}1' file |
awk '!/filter-shared 14/ && /move-group/{print $1; print $2; print x}' FS='\n' RS=

Hi.

There is a perl version of grep called peg available at: http://www.cpan.org/authors/id/A/AD/ADAVIES/peg-3.10 , so using peg -A1 "interface" in place of the grep produces:

 Results:
interface 1/1/1/1 move-group
     decription one one one one
--
interface 1/1/1/3 move-group
     decription one one one three

Best wishes ... cheers, drl

Scrutinizer, your solution works but it is so slow. I have 4400 large files to process and the awk statement I had been using was very fast. If I could just get it to add the next line, it would be perfect.

nawk 'END {if (!ok) print r} /interface/ && /move-group/ {if (!ok) print r; r = $0; ok = x}/filter-shared 14/ {r = r RS $0; ok++}' inputfile

Try:

nawk '/interface/&&/move-group/{if(s)print s; s=x; n=2} n-->0{s=s $0 ORS} /filter-shared 14/{s=x; n=0} END{if(s)print s}' file
1 Like