6

I want to use the tacto reverse a text file character by character. On the info page for coreutils I found an example saying: #Reverse a file character by character tac -r -s 'x\|[^x]'

However running tac -r -s seems to open standard input instead of printing the file. What does 'x\|[^x]' mean and what should I be doing?

I also noted that the output for tac [file] and tac -r [file] are same and they're the same as cat [file]. Still can't figure out char by char reverse.

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
Weezy
  • 599

2 Answers2

11

To reverse a file character-by-character using tac, use:

tac -r -s 'x\|[^x]'

This is documented in info tac:

# Reverse a file character by character.
tac -r -s 'x\|[^x]'

-r causes the separator to be treated as a regular expression. -s SEP uses SEP as the separator. x\|[^x] is a regular expression that matches every character (those that are x, and those that are not x).

$ cat testfile
abc
def
ghi
$ tac -r -s 'x\|[^x]' testfile

ihg
fed
cba%
$

tac file is not the same as cat file unless file has only one line. tac -r file is the same as tac file because the default separator is \n, which is the same when treated as a regular expression and not.

Michael Homer
  • 76,565
  • 1
    Wouldn't . work just as well as the regex? What does this match that . doesn't? \n? – muru May 28 '18 at 09:06
  • 2
    @muru Yes, if you use . it swaps bytes over the \n: printf 'ab\ncd\n'|tac -r -s . |hexdump -c -> \n d \n c b a, and tac -r -s . | tac -r -s . isn't idempotent. – Michael Homer May 28 '18 at 09:13
4

If you're not concerned about the LF character (see mosvy's comments), then it is much more efficient to use rev rather than tac -r -s 'x\|[^x]', piping it to tac if needed.

$ cat testfile
abc
def
ghi
$ rev testfile
cba
fed
ihg
$ rev testfile|tac
ihg
fed
cba

This solution is way cheaper than tac -r -s 'x\|[^x]' because regular expressions tend to slow things down quite dramatically.

  • 1
    Indeed. A ~tenfold speedup. But it's not actually reversing the input character by character: in printf "foo\nbar\n" | rev | tac, the output will not start with "\n" and end with "f", but start with "r" and end with "\n" ;-) –  Dec 13 '19 at 21:22
  • 1
    you can replace the rev with perl -ne 'print scalar reverse', though. –  Dec 13 '19 at 21:31
  • @mosvy Thanks for the hint, added a warning. ;) – Skippy le Grand Gourou Dec 13 '19 at 21:43