5

Other than the obvious syntactical point that b can accept a label while n does not, and that to use b without a label you need to place it at the end of an argument passed with -e (for one-liners, at least)...a b without a label and an n seem to do the exact same thing.

Is this actually true, or are there subtle differences I haven't noticed? What are the conditions under which they do the same thing and the conditions under which they don't?

  • 1
    Not sure how you'd think of them as identical. b without a label starts next cycle, while n reads the next line from the input and keeps going. Compare f.i. the results of seq 1 5 | sed '/2/ { b; d; }' and seq 1 5 | sed '/2/ { n; d; }'. – Satō Katsura Jul 7 '16 at 21:14
  • @SatoKatsura, actually I think it was partially a confusion between Sed's n command and Awk's next command. I now realized that Awk's next command is actually equivalent to the delete command (d) in Sed. :) – Wildcard Nov 18 '16 at 2:23
  • Awk's next isn't exactly equivalent to sed's d either. A guy named Banach came up with the hierarchy of theories, analogies between theories, and analogies between analogies. He didn't quite get to analogies between apples and orange juice. Now, that would make for an entertaining development. :) – Satō Katsura Nov 18 '16 at 4:43
  • @SatoKatsura, only because Awk has no such thing as auto-print. Otherwise they're equivalent. Right? – Wildcard Nov 18 '16 at 5:17
  • No, because sed has a pattern space and a hold space, while awk has records and the associated bookkeeping. Basic thing to understand about computers and algorithms: data structures always trump behavior. The former are measurables. The latter isn't. – Satō Katsura Nov 18 '16 at 5:27
4

Well, let's see...
the branch command:

[2addr]b[label]
    Branch to :label. If label is not specified, branch to the end of the script.

the next command:

[2addr]n
    If auto-print is not disabled, print the pattern space, then, regardless,
    replace the pattern space with the next line of input. If no next line of
    input is available, branch to the end of the script.

So the main difference here is:

b unconditionally branches to the end of script
n only branches to the end of script if there's no more input

Assuming an input like:

fdue
four
four
fdue
four

and that I wanted to replace f with t but only on lines that match four and on all other lines replace u with i. One way to do this is

sed '/four/{s/f/t/;b;};s/u/i/' infile

and the output is, as expected

fdie
tour
tour
fdie
tour

now, let's see what happens when using n instead of b:

fdie
tour
foir
fdie
tour

The second line matching four was edited to foir instead of tour for the simple reason n does not return to the top of the script. Instead, the s command following the n processed the line even thought this was not intended. However, on the last line, only the 1st substitution was made so the s command after the n was no longer executed.

To sum up, these two commands could be functionally equivalent if:

  • the script was processing the last line of input (before executing n) or
  • the next line pulled in by n was not supposed to be edited by the commands that precede n1 anyway (in other words, the next line was not an address that's associated to b)

It's true that n prints the current pattern space and pulls in the next line but b alone also has the same effect: branching to end of script means auto-printing (unless sed was invoked with -n, in which case n doesn't print either) and then automatically reading in the next line of input. The main difference, as I said, is that n doesn't jump to end of script (and it doesn't return to top of the script either): sed simply executes the remaining commands - if any (and if not on the last line).


1
Those commands won't be executed since, as I said, n doesn't return to the top of the script; also, the remaining commands might edit the line which is something that wasn't supposed to happen.

  • I'm accepting your answer because it was the most helpful, but actually none of the answers made it clear to me at the time. Now I just reviewed this and got what I was missing: Neither n nor b have anything to do with printing. Each of them prints or not according to whether -n was set. The difference is that n continues from that point in the commands with the next line of input, whereas b starts from the beginning of the Sed commands with the next line of input. I've had to edit an answer of mine now I got it. – Wildcard Jul 18 '17 at 8:07
6

These two examples are the same except that n was replaced by b. You can see that the output is different:

$ echo $'1\n2\n3' | sed 's/./AA/; n; s/./BB/'
AA
BB
AA
$ echo $'1\n2\n3' | sed 's/./AA/; b; s/./BB/'
AA
AA
AA

b branches to the end of the commands. n continues on with the remaining commands. Thus, with b, the substitution s/./BB/ is never executed. With n, it is executed for every other line.

(The above commands were run using GNU sed. With BSD/OSX sed, as the OP notes, some minor changes to the code format would be needed.)

3

The "branch" command (b) branches to a predefined label in the editing script or to the end of the script if there's no label, while the "next" command (n) reads the next line of input. The "next" command also outputs the current pattern space (usually the line just read) before reading the next line unless output has been suppressed with -n.

They are two different editing commands that do two different things. The "next" command does not cause a branch to the end of the script and the "branch" command doesn't output the current pattern space or read any new line by itself.

A simple example (using GNU sed):

$ cat lines
1
2
3
4
5

$ gsed -n '3{n;p;p;}' lines
4
4

The script will, for the third line, read the next line and output it twice: The fourth line is printed twice.

$ gsed -n '3{b;p;p;}' lines

This will not generate any output because the script will branch past the "print" (p) commands.

Running without -n:

$ gsed '3{n;p;p;}' lines
1
2
3
4
4
4
5

The third line is outputted by the n command in the script. The fourth line is outputted three times, twice by the two p in the script after n has read the next line, and once by the default p command.

$ gsed '3{b;p;p;}' lines
1
2
3
4
5

The lines are all outputted by the default p command as the "branch" command will bypass the two p in the script.

To summarise: n loads the next line and continues processing with the next instruction, while b skips to the named branch (if named) or to the end of the script. In either case, when sed gets to the end of the script, any remaining input is processed from the beginning of the script as usual.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.