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.