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
  • 66,199
  • 35
  • 114
  • 250
Weezy
  • 529
  • 1
  • 6
  • 16

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
  • 74,824
  • 17
  • 212
  • 233
  • 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