Krischu
Krischu

Reputation: 1135

sed command to delete two subsequent lines in a file

My file contains lines like

GRANT SELECT ON USER.TABLE1 TO USER1
/
GRANT INSERT ON USER.TABLE1 TO USER1
/
GRANT UPDATE ON USER.TABLE1 TO USER1
/
GRANT DELETE ON USER.TABLE1 TO USER1
/
GRANT SELECT ON USER.TABLE1 TO USER2
/
GRANT INSERT ON USER.TABLE1 TO USER2
/
GRANT UPDATE ON USER.TABLE1 TO USER2

... and so on ...

The sed command I'm seeking, or better, the pattern, that crosses two lines, should delete the "GRANT .." line plus the subsequent line that's starting with the "/".

Any solutions from you sed gurus?

Upvotes: 0

Views: 115

Answers (3)

Ed Morton
Ed Morton

Reputation: 204099

sed is for operations on single lines. For anything that spans lines you should use awk. e.g. with GNU awk for multi-char RS:

awk -v RS='^$' -v ORS= '{gsub(/GRANT[^\n]+\n[/]\n/,"")}1' file

e.g.:

$ cat file
foo
GRANT INSERT ON USER.TABLE1 TO USER1
/
GRANT UPDATE ON USER.TABLE1 TO USER1
intermediate line so leave
/
GRANT DELETE ON USER.TABLE1 TO USER1
/
bar

$ awk -v RS='^$' -v ORS= '{gsub(/GRANT[^\n]+\n[/]\n/,"")}1' file
foo
GRANT UPDATE ON USER.TABLE1 TO USER1
intermediate line so leave
/
bar

Upvotes: 0

Wintermute
Wintermute

Reputation: 44063

sed '/^GRANT/ { N; d }' filename

will, when it encounters a line beginning with GRANT, fetch the next line and discard both.

To only remove GRANT lines when the next line begins with /,

sed ':a /^GRANT/ { N; /\n\//! { P; s/.*\n//; ba }; d }' filename

works. This is complicated mostly because of the possibility that two GRANT lines come directly one after another, as in

GRANT foo
GRANT bar
/

where GRANT bar and / have to be removed and GRANT foo to remain untouched:

:a            # jump label for looping
/^GRANT/ {    # if a line begins with GRANT
  N           # fetch the next line
  /\n\//! {   # if the next line does not begin with /
    P         # print the first
    s/.*\n//  # remove it
    ba        # go back to :a
  }
  d           # otherwise discard both
}

Alternatively, using a multiline regex after reading the file completely into memory (assuming it fits there):

sed ':a $!{N; ba}; s/\(^\|\n\)GRANT[^\n]*\n\/[^\n]*//g' filename

where

:a $!{N; ba}                          # read whole file into the pattern space
 s/\(^\|\n\)GRANT[^\n]*\n\/[^\n]*//g  # match offending lines by regex and
                                      # remove them

At this point, however, I feel that the sanest option is to use pcregrep in muiltiline mode:

pcregrep -vM '^GRANT[^\n]*\n/' filename

Upvotes: 2

Jotne
Jotne

Reputation: 41460

This awk should work:

awk '/^GRANT/ {f=1;next} f && /^\// {f=0;next} 1' file

If the line starting with ^GRANT, skip it and set flag f to true.
If line starts with / and flag f is true, skip it and reset flag f
1 print all other lines.

So this will only delete lines starting with GRANT and the next line starting with /

Example:

cat file
GRANT SELECT ON USER.TABLE1 TO USER1
/
GRANT INSERT ON USER.TABLE1 TO USER1
test
/ should be removed
some other data
/ should not be removed
GRANT UPDATE ON USER.TABLE1 TO USER1
/
GRANT DELETE ON USER.TABLE1 TO USER1
/
GRANT SELECT ON USER.TABLE1 TO USER2
/
GRANT INSERT ON USER.TABLE1 TO USER2
/
GRANT UPDATE ON USER.TABLE1 TO USER2

awk '/GRANT/ {f=1;next} f && /^\// {f=0;next} 1' file
test
some other data
/ should not be removed

Upvotes: 0

Related Questions