12

-b, --before

The separator is attached to the beginning of the record that it precedes in the file.

And I can't understand the following output:

$ echo -e "Hello\nNew\nWorld\n!" > file
$ tac file
!
World
New
Hello
$ tac -b file


!
World
NewHello

Why there is no newline between New and Hello?

SantaXL
  • 355
  • 1
  • 15

2 Answers2

23

tac works with records and their separators, attached, by default after the corresponding record. This is somewhat counter-intuitive compared to other record-based tools (such as AWK) where separators are detached.

With -b, the records, with their newline attached, are as follows (in original order):

  • Hello
  • \nNew
  • \nWorld
  • \n!
  • \n

Output in reverse, this becomes

\n\n!\nWorld\nNewHello

which corresponds to the output you see.

Without -b, the records, with their newline attached, are as follows:

  • Hello\n
  • New\n
  • World\n
  • !\n

Output in reverse, this becomes

!\nWorld\nNew\nHello\n
Stephen Kitt
  • 411,918
  • 54
  • 1,065
  • 1,164
5

That does look strange partly due tac works in somewhat counter-intuitive way as to me. Let's make use of clearly visible separators — commas.

What would I expect to see as the result of the following command:

% echo -ne 'A,B,C' | tac -s,

?

Well, I see it as there's A separated from B separated from C. Thus (I conclude) being printed in reverse, they should constitute C,B,A. Let's check. Alas, it prints differently instead:

% echo -ne 'A,B,C' | tac -s,
CB,A,

We can conclude tac sticks for original separators placement: A still has it afterwards, as well as B does, but C didn't have it and hence it's printed as is.

What happens if I run tac with -b this time?

% echo -ne 'A,B,C' | tac -bs,
,C,BA

Seemingly it works this way for -b: it's going through the input in backward direction till it finds a separator. As it's found it's printed:

,. Then it prints the text it skipped while searching:

C. Then the cycle repeats:

,B. As there're no separators left, just the remainder is printed:

A.

Why there is no newline between New and Hello?

According to the explanation I've given above New would be on a new line because it was prefixed with new-line, but since Hello wasn't — it'd be printed as is.

poige
  • 6,195
  • 2
  • 30
  • 57
  • Regarding your first part, it helps to understand `tac`’s counter-intuitive behaviour if you consider that its initial goal was to reverse text files, which end with a `\n`. The configurable separator does suggest that it can reverse records separated by arbitrary characters, in which case you don’t expect the text to end with a separator, but it doesn’t work that way :-(. Your explanation of `-b` is a fairly accurate description of coreutils’ implementation of `tac`; note however that `tac` always searches backwards (even without `-b`). – Stephen Kitt Oct 22 '19 at 09:30
  • Indeed tac is always tac, even w/o `-b` – poige Oct 22 '19 at 09:39