1

I have a file with visibility declarations, spanning over one or more lines, that I want removed only if they are in a test block.

i.e. input.txt:

test(
    srcs = [
        "test1",
    ],
    visibility = [
        "common",
    ],
    deps = [ "deps" ],
)

test(
    srcs = [
        "test2",
    ],
    visibility = [ "common" ],
)

i.e. output:

test(
    srcs = [
        "test1",
    ],
    deps = [ "deps" ],
)

test(
    srcs = [
        "test2",
    ],
)

The visibility lines could be inside other blocks i.e. etc(...), in which case they should not be removed. i.e.:

etc(
    src = [
        "etc",
    ],
    # should not be removed because it's not inside a test(...) block
    visibility = [
        "common",
    ],
)

This is what I have tried, however, this only matches visibility blocks spanning over a single line:

#!/bin/bash
#remove_lines.sh
remove_visibility_lines () {
    start_pattern="test"
    end_pattern=")"

    pattern_to_remove="visibility = \[.*\],"

    sed "/${start_pattern}/,/${end_pattern}/{/${pattern_to_remove}/d}" "$1"
}

remove_visibility_lines $1

$ ./remove_lines.sh input.txt

I've tried several ways to get this to remove visibility spanning over multiple block, i.e. (.*?) and (\_.*), but I can't seem to get it to work. Please help?

Question is similar to: Using sed to delete all lines between two matching patterns , however, in my case I have patterns nested inside patterns. I.e.: you only look inside the test(...) block, and only inside those blocks you remove the visibility = [...], blocks.

oguz ismail
  • 1
  • 16
  • 47
  • 69
Sanda
  • 1,357
  • 19
  • 29
  • `(.*?)` is undefined behavior in sed and awk, I suspect it probably means something in perl but idk. Please google BREs (as used by default in all sed variants), EREs (awk and GNU or BSD sed), and PCREs (perl). – Ed Morton Sep 15 '21 at 17:17

1 Answers1

2

With awk, assuming no nested () characters:

awk '/^test\(/{f=1} $0==")"{f=0}
     f && /visibility = \[/{v=1} !v; /],/{v=0}' ip.txt
  • /^test\(/{f=1} set flag f if a line starts with test(
  • $0==")"{f=0} clear flag f if a line contains ) only
  • f && /visibility = \[/{v=1} set flag v if f is also set and a line contains visibility = [
  • !v; to print input lines only if flag v is not set
  • /],/{v=0} clear flag v if line contains ],

To pass start and end strings:

$ cat script.awk
index($0, start) == 1 { f = 1 }
$0 == end { f = 0 }
f && /visibility = \[/ { v = 1 }
! v { print }
/],/ { v = 0 }

$ awk -v start='test(' -v end=')' -f script.awk ip.txt
Sundeep
  • 23,246
  • 2
  • 28
  • 103