Search code examples
sed

Sed-Script to comment out lines between two multiline markers A\nB and U\nV


I have the file "test" with the content in (1). I want a sed script, that edits "test" so that i have the output in (2).

I cannot not simply do this:

cat test | sed -E '1,/B/!{/U/,$!s/.+/# &/}'

...because it would start commenting out beginning from first 'B' and not from 'A\nB'.

(1)

X
X

B

A
B

X
X
X

U

U
V

X
X

(2)

X
X

B

A
B

# X
# X
# X

# U

U
V

X
X

Solution

  • Sed is Turing-complete, so you can do anything with it that you can do with any other programming language. Which means that you can have a complete solution to the original problem.

    In fact, Sed has functions. Functions are just pieces of codes you can jump to and jump back from. The point is knowing where to jump to, forth and back. Setting :labels, branching to them or end of script unconditionally, or jumpting conditionally based on the result of a test is what allows you to write loop and functions in Sed.

    Here's a relatively short one-liner accomplishing the original task.

     sed '/A/{N;s/A\nB/&/;tc};b;:c;n;/U/{N;s/U\nV/&/;t};s/^./\# &/;s/\n/&\# /;bc' file
    

    Let me explain using pseudo-code

    • /A/: if line matches A, do {
      • N: append the next line to the current one;
      • s/A\nB/&/: check if A\nB matches (don't change it, just set the flag for the next t command);
      • t:est if the flag is set; if so, call the :commenting function
    • }: otherwise,
    • b:ranch to end of script, i.e. print line as is and start over with the next line;
    • :c:ommenting function:
      • n, print what you have in pattern space and read the next line
      • /U/: if line matches U, {
        • N: append the next line to the current one;
        • s/U\nV/&/: check if U\nV matches (...);
        • t: test if the flag is set and; if so, go to end of script, and start over with the next line, going out of the commenting code, }
      • s/^./\# &/ comment non-empty lines
      • s/\n\(.\)/&\# \1/ also comment non-empty line following a line match U in case the former was not a V
      • branch to :commenting function, i.e. recursive call.