Remove all lines between two similar patterns if a specific pattern in between is not matched

The title should be: Remove all lines between two similar patterns, including the line of the starting pattern. if another specific pattern in between is not matched. I made it shorter because its more readable for others searching for answers on Google.

About the questions: I have a list of listable shares and unreable shares for a specific computer. I want to create a list of all computers that have listable shares and delete their unreadable shares. But I stumble two problems. Firstly, with correctly deleting all lines between two simular patterns. Secondly, how not to delete the lines between two simular words if a specific pattern is found.

My input is

Shares for DED-SHD-ED-5:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        E$
        H$
        IPC$

Shares for DED-SHD-ED-6:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        print$
Shares for DED-SHD-ED-7:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        backup      backup2
Shares for DED-SHD-ED-8:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$

Shares for DED-SHD-ED-9:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
        VBRCatalog
    [--- Listable Shares ---]
        backup      backup2

I managed to strip all unreadable shares with the following command awk '/Listable Shares/,/Shares for/' input.txt, the problem with this command is that DED-SHD-ED-8 have no Listable Shares. It strips the computer DED-SHD-ED-9 below and I will see the Listable shares of DED-SHD-ED-9 at DED-SHD-ED-8

See the output below (I know the name of the first computer in the list is missing but this is not an issue for me)

    [--- Listable Shares ---]
        print$
Shares for DED-SHD-ED-7:
    [--- Listable Shares ---]
        backup      backup2
Shares for DED-SHD-ED-8:
    [--- Listable Shares ---]
        backup      backup2

To solve this issue I had the idea the strip all computers WITHOUT Listable shares before I run awk '/Listable Shares/,/Shares for/' input.txt. At first I tried to delete all lines between "Shares for" and "Shares for". I looked around on the forum and tried to make the more simple answers to work and avoided the complicated ones because they go out of my understanding at the moment. For example I used this command sed '/^Shares for/,/^Shares for/{//!d;};' input.txt and sed '/Shares for/,/:/{//!d;};' input2.txt In here it strips half of the lines between the computers.

Output

Shares for DED-SHD-ED-5:
Shares for DED-SHD-ED-6:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        print$
Shares for DED-SHD-ED-7:
Shares for DED-SHD-ED-8:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$

Why it strips the data of only half of the computers? I don’t see why

My next idea was to add an excluding to the command to not delete the lines between "Shares for" and "Shares for" if the pattern Listable is matched in these lines. And then delete the computers with no data left like DED-SHD-ED-5 and DED-SHD-ED-7. But maybe this is not the best approach. Maybe its better to delete the line of the first pattern is the pattern "Listable" is not seen.

I would love some help and insights how to propely process this all.

Expected output:

Shares for DED-SHD-ED-6:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        print$
Shares for DED-SHD-ED-7:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        backup      backup2
Shares for DED-SHD-ED-9:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
        VBRCatalog
    [--- Listable Shares ---]
        backup      backup2
Asked By: pwrsheller

||

Does this work for you? shares being the file-name I picked for your sample input.

sed -e 'tD' -e '$!N;/.nShares/s/n/&&/;:D' -e 'P;D' shares | awk 'BEGIN{RS="nn";FS="n"}/Listable/'
Shares for DED-SHD-ED-6:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        print$
Shares for DED-SHD-ED-7:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        backup      backup2
Shares for DED-SHD-ED-9:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
        VBRCatalog
    [--- Listable Shares ---]
        backup      backup2

The sed basically inserts a newline before Shares if there isn’t one already – that’s so we can use newlines as the record separator (RS="nn") in awk. Then it is simply a question of only printing records that have the word Listable in them.

Answered By: tink

Understanding:

  • print a Shares block if said block contains the string Listable Shares

Once you bring awk into the mix you can usually eliminate the need for sed.

One (verbose) awk idea:

$ cat shares.awk

NF == 0           { next }                        # skip blank lines
$1 == "Shares"    { if (print_block)              # if 1st field is "Shares"; if flag is set (==1) then ...
                       print block                # print the previous block
                    print_block = 0               # clear the flag
                    block = $0                    # initialize a new block
                    next                          # skip to next input line
                  }
/Listable Shares/ { print_block = 1 }             # if line contains "Listable Shares" then set flag
                  { block = block ORS $0 }        # append current line to end of block
END               { if (print_block)              # flush last block to stdout?
                       print block
                  }

Taking for a test drive:

$ awk -f shares.awk input.txt
Shares for DED-SHD-ED-6:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        print$
Shares for DED-SHD-ED-7:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        backup      backup2
Shares for DED-SHD-ED-9:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
        VBRCatalog
    [--- Listable Shares ---]
        backup      backup2
Answered By: markp-fuso

Using Raku (formerly known as Perl_6)

~$ raku -e '.put for slurp.comb(/^^Shares .*? <?before ^^Shares | $ >/).grep(/Listable/);'   file 

#OR:

~$ raku -e '.put for lines.join("n").comb(/^^Shares .*? <?before ^^Shares | $ >/).grep(/Listable/);'   file

Raku is a programming language in the Perl-family. In an operation which might be considered the converse of destructive splitting on a regex pattern, Raku provides the comb routine which allows you to select out elements of your choosing. Above, The input file is broken into records by .comb(/^^Shares .*? <?before ^^Shares | $ >/) selecting for records that have:

  • ^^Shares start-of-line text "Shares",
  • .*? non-greedy any number of characters,
  • <?before ^^Shares | $ > a positive lookahead which says to stop the pattern ?before the next ^^Shares record pattern is seen, or $ before/at the end of the file itself.

In the second operation in the chain grep is used to return only those records that contain a Listable block.

Sample Input:

Shares for DED-SHD-ED-5:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        E$
        H$
        IPC$

Shares for DED-SHD-ED-6:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        print$
Shares for DED-SHD-ED-7:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        backup      backup2
Shares for DED-SHD-ED-8:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$

Shares for DED-SHD-ED-9:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
        VBRCatalog
    [--- Listable Shares ---]
        backup      backup2

Sample Output:

Shares for DED-SHD-ED-6:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        print$

Shares for DED-SHD-ED-7:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
    [--- Listable Shares ---]
        backup      backup2

Shares for DED-SHD-ED-9:
    [--- Unreadable Shares ---]
        ADMIN$
        C$
        IPC$
        VBRCatalog
    [--- Listable Shares ---]
        backup      backup2

Note: It is often instructive to look at how a language internally represents data, so here is the output after combing into separate records but before grepping for the desired block:

~$ raku -e '.raku.put for slurp.comb(/^^Shares .*? <?before ^^Shares | $ >/);'   file
"Shares for DED-SHD-ED-5:n    [--- Unreadable Shares ---]n        ADMIN$n        C$n        E$n        H$n        IPC$nn"
"Shares for DED-SHD-ED-6:n    [--- Unreadable Shares ---]n        ADMIN$n        C$n        IPC$n    [--- Listable Shares ---]n        print$n"
"Shares for DED-SHD-ED-7:n    [--- Unreadable Shares ---]n        ADMIN$n        C$n        IPC$n    [--- Listable Shares ---]n        backup      backup2n"
"Shares for DED-SHD-ED-8:n    [--- Unreadable Shares ---]n        ADMIN$n        C$n        IPC$nn"
"Shares for DED-SHD-ED-9:n    [--- Unreadable Shares ---]n        ADMIN$n        C$n        IPC$n        VBRCatalogn    [--- Listable Shares ---]n        backup      backup2nn"

https://docs.raku.org/language/operators#Operators
https://docs.raku.org/routine/comb
https://docs.raku.org/routine/grep
https://raku.org

Answered By: jubilatious1