Explanation of tac --before

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

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.

Tags:

Coreutils

Tac