Skip to content
 

Debugging sed programs

To understand why and how a sed program works (and how sed programs work in general, for that matter), I suggest to start with an unusal approach, very low-tech. Sit down with paper and pencil, draw some boxes to represent the pattern and the hold spaces (the hold space is not used in this example), and try to simulate the behavior of the program with some simple input.

Let's debug this sed program:

sed ':begin;$!N;/PATTERN\n/s/\n//;tbegin;P;D'    # if the line ends in PATTERN, join it with the next line

The input is like this:

line1
line2foo
line3foo
line4
line5foo
line6

and we want to join lines if a line ends with "foo". Our sed program will thus become:

sed ':begin;$!N;/foo\n/s/\n//;tbegin;P;D' file.txt

Let's go through the input, see what sed does, and what the pattern space contains at each step. When the program starts, the first line of input is in the pattern space:

PATTERN SPACE: line1

:begin is just a label, so no action is performed. Then $!N reads in a new line if the current line isn't the last in the input, so now we have:

PATTERN SPACE: line1\nline2foo

Now /foo\n/ checks whether we have "foo" followed by a newline in the pattern space, which we don't. So the s command is not run, and the following t test is not true since no substitution was performed. So sed executes P and D, which respectively print line1 and deletes it from the pattern space, leaving

PATTERN SPACE: line2foo

D starts a new cycle, so sed goes back to the beginning of the program, without reading any new line. Again, $!N reads in a new line, since we're not at the last line, so we have

PATTERN SPACE: line2foo\nline3foo

Now /foo\n/ does match, so the substitution is performed, giving

PATTERN SPACE: line2fooline3foo

Also, since the substitution was performed, the t test is true, so the control flow jumps to the label begin, ie again at the beginning. Nothing was printed, nor deleted, and the pattern space is still as we left it. Now again, $!N reads in another line, yielding

PATTERN SPACE: line2fooline3foo\nline4

Again /foo\n/ matches here, so the substitution is performed, giving

PATTERN SPACE: line2fooline3fooline4

the t test is true, and we're back at the beginning. You'll notice that the pattern space is accumulating lines. $!N reads in a new line (since we're not at the end of the file yet):

PATTERN SPACE: line2fooline3fooline4\nline5foo

Now we don't have a match for /foo\n/, so the s is skipped, and t is false, which leads us to P and D. Not surprisingly, the first prints line2fooline3fooline4, and the second leaves the pattern space as follows:

PATTERN SPACE: line5foo

and starts a new cycle. $!N reads another line, giving

PATTERN SPACE: line5foo\nline6

We have a match, and the \n is removed, thus

PATTERN SPACE: line5fooline6

The t sends us once again to the beginning. Now we really are at the end, so N is not executed and no line is read. We don't have a match for /foo\n/, so sed skips directly to P, which prints line5fooline6, and D, which deletes the whole pattern space. Since we are at the end of the input, the program terminates. The final output thus is

line1
line2fooline3fooline4
line5fooline6

which satisfies our initial requirements to join lines ending in "foo" with the next line. You can use a similar method to trace the actions performed by any sed program.

Uhm, I think sed is not for me...

Wait! Now that we've gone through the pain of understanding the gory details, and that hasn't scared you away from sed, you will be happy to know that a sed debugger that automatically does exactly what we did by hand exists: it's called sedsed. It's a great piece of software, very easy to use, and you can find it here.