3

I'm reading sed & awk book and I thought that I understood the n command of sed till I executed the following:

$ echo -ne "abc\ncde\nfg\n" | sed "/c/{
n
/f/d
}"

Output:

abc
cde
fg

But I expected the line fg to be deleted.

My understanding of the process:

  • c matches the line cde, the next line is fg and should be deleted by the /f/d command as it is matched by f.

I was so sure that I understand this command and it is simple. Especially because the authors write that the uppercase commands N,D,P commands are more difficult and if you understand them then you understand the lowercase commands anyway. But I have no difficulties with the N command at all.

cuonglm
  • 153,898
ka3ak
  • 1,255
  • Think what you wanted was sed '/c/{N;P;/f/!{D};d}'. – 123 Jul 22 '16 at 09:18
  • 1
    As I said here n doesn't return to the top of the script (i.e. it doesn't restart the command cycle), it just runs the remaining commands (so the conditional block /c/{..} is never applied to the cde line); and just for fun: edit the last line to read cfg and you'll see it won't be deleted, even if it does match /c/ and /f/ – don_crissti Jul 22 '16 at 09:21
  • I think you script says delete any record with an f in it that follows an odd number of records with a c in it. – ctrl-alt-delor Jul 22 '16 at 09:33

2 Answers2

6

The problem, your line fg is never matched by the pattern /f/.

The first line abc matched /c/, then the commands inside block are executed.

  • The n command print the current pattern space, which is abc and replace the pattern space with next line of input, which is cde.

  • cde doesn't match /f/, then it was not deleted, and printed to standard out.

Now, next line of input is fg was read, doesn't match /c/, then the code block was not executed.

You can verify by using look command:

echo -ne "abc\ncde\nfg\n" | sed -n "l;/c/{
n
/f/d
}"

outputs:

abc$
fg$

Adding look command at the beginning of script tells you which lines of input was processed outside of code block.

cuonglm
  • 153,898
2

After reading the comment of @don_crissti I think I understand how n command affects flow control of sed.

n excludes the read (or next) line from the analysis ..."/c/{... in the next cycle. So if use n 2 times in my example, abc will be matched then cde will be read, then fg will be read and deleted.

$ echo -ne "abc\ncde\nfg\n" | sed "/c/{
n
n
/f/d
}"

Output:

abc
cde
ka3ak
  • 1,255
  • No, n does not exclude anything. It outputs the current line (unless -n is used) and reads the next line. The second n outputs the second line and reads the fg line. Then the /f/ matches and the d deletes. – Kusalananda Jul 22 '16 at 11:00
  • @Kusalananda - you misread the post here... n "excludes" the commands that precede it from being applied to the line that was just pulled in because it doesn't return to the top of the script. ka3ak phrasing is far from ideal but the message is quite clear (at least to me). – don_crissti Jul 22 '16 at 11:17
  • @don_crissti Ah, well, yes, that's a "feature" of n in a way, at least in this case. – Kusalananda Jul 22 '16 at 11:27
  • @Kusalananda - absolutely, it's actually the feature that trips most people up. – don_crissti Jul 22 '16 at 11:30
  • "top of the script" is also mentioned in the book but I couldn't understand what it means. All I wanted to say is that you have to do something with the line returned by 'n' between 'n' and closing curly brace because the next cycle (..."/c/{...) will skip it. – ka3ak Jul 22 '16 at 18:27