30
\$\begingroup\$

Between the kitten question and seeing this question at U&L about some sed magic, how about implementing tac?


Objective

Implement a program that will reverse and print the lines in a file.


Input

A file, provided as a name or via standard input


Output

The lines, reversed, to standard out.


Scoring

Bytes of source code.

\$\endgroup\$
8
  • 10
    \$\begingroup\$ tac is a bit strange when it comes to trailing linefeeds. It transforms a\nb\n (trailing linefeed) into b\na\n and a\nb (no trailing linefeed) into ba\n. Is this how our code it supposed to behave? \$\endgroup\$
    – Dennis
    Commented Oct 1, 2015 at 6:34
  • \$\begingroup\$ Related \$\endgroup\$ Commented Oct 1, 2015 at 6:35
  • 11
    \$\begingroup\$ Also, if we have to replicate tac's behavior, a 3 byte Bash answers that executes tac is only a matter of time... \$\endgroup\$
    – Dennis
    Commented Oct 1, 2015 at 6:50
  • 1
    \$\begingroup\$ @Dennis at this point probably best to leave undefined. \$\endgroup\$
    – Nick T
    Commented Oct 1, 2015 at 15:36
  • 1
    \$\begingroup\$ @Dennis Makes sense to me. Visualize the lines of a file as horizontal rows, all ending with \n. tac reverses the order of these rows. If an \n is removed from the middle of the file, the row it terminated is joined to the next row, but in the case of the last line, there is no next row to join to. \$\endgroup\$ Commented Oct 1, 2015 at 19:43

43 Answers 43

15
\$\begingroup\$

GS2, 3 bytes

* +

The three bytes are, in order, split lines, reverse, and join lines.

\$\endgroup\$
9
\$\begingroup\$

Perl, 11 bytes

$\=$_.$\}{

Behaves exactly like tac. This code requires the -p switch, which I have counted as 1 byte.

Test runs

$ echo -en 'a\nb' | perl -pe'$\=$_.$\}{' | xxd -g 1
0000000: 62 61 0a                                         ba.
$ echo -en 'a\nb\n' | perl -pe'$\=$_.$\}{' | xxd -g 1
0000000: 62 0a 61 0a                                      b.a.

How it works

As explained here, the -p switch basically wraps while (<>) { ... ; print } around the program, so the source code is equivalent to

 while(<>)
 {
   $\ = $_ . $\
 }
 print

For each line of input, we prepend the current line ($_) to $\ (initially undefined), updating the latter with the result.

After all lines have been processed, print prints the value of the local variable $_ (undefined in this scope), followed by the output record separator ($\).

\$\endgroup\$
11
  • \$\begingroup\$ Care to explain how this works? \$\endgroup\$
    – xebtl
    Commented Oct 1, 2015 at 8:21
  • 2
    \$\begingroup\$ @xebtl Evilly. Adding the -p switch wraps your code in a loop that begins while(<>){ and ends } continue { print }, which allows filtering input just by modifying $_. $\=$_.$\ prepends each line of input to the output record terminator, and the }{ ends the perl-supplied while block prematurely, so the continue block is no longer attached to it. So all the lines of input are added to $\ in reverse order, then at the end the continue { print } eventually runs, printing "nothing" ($_ will be undef after the end of input), but with a terminator of $\. \$\endgroup\$
    – hobbs
    Commented Oct 1, 2015 at 14:47
  • \$\begingroup\$ @xebtl grr, code formatting in comments seems a bit broken where backslashes and backticks come near each other. Perhaps you can guess at what I was trying to say. \$\endgroup\$
    – hobbs
    Commented Oct 1, 2015 at 14:49
  • 1
    \$\begingroup\$ @primo The first example shows what happens in this case. The output will be strange, but exactly like tac's. \$\endgroup\$
    – Dennis
    Commented Oct 2, 2015 at 14:30
  • 1
    \$\begingroup\$ @Dennis pages 18 ff of this book \$\endgroup\$
    – msh210
    Commented Jun 2, 2016 at 2:45
8
\$\begingroup\$

Pyth, 4 bytes

j_.z

.z is the input separated by lines as a list, _ reverses it and j joins it by a character, which by default is \n.

\$\endgroup\$
0
8
\$\begingroup\$

FlogScript, 2 bytes

)"

(Try it on anarchy golf.)

The ) enables --in-out-line-array mode, and the rest of the program is ", reversing the array of lines.

\$\endgroup\$
1
  • \$\begingroup\$ Argh, you beat me to it! \$\endgroup\$
    – mbomb007
    Commented Oct 1, 2015 at 14:15
7
\$\begingroup\$

Retina, 7 bytes

!rm`.*$

With a single regex, Retina runs in Match mode. This normally just prints the number of matches, but with ! we configure it to print the actual matches instead (separated by linefeeds).

The actual regex is merely .*$. .* matches any line (potentially empty), because . can match any character except linefeeds. I'll get to the $ in a minute.

How do we make it print the matches in reverse? By making use of .NET's right-to-left matching mode, activated with the r. This means the regex engine starts at the end of the string when looking for matches and works backwards.

Finally, the m makes the $ match the end of a line instead of the end of the string. Why do we even need that? The trouble is that .* generates extraneous matches. Consider the regex substitution

s/a*/$0x/

applied to the input baaababaa. You'd think this would yield baaaxbaxbaax, but it actually gives you baaaxxbaxxbaaxx. Why? Because after matching aaa the engine's cursor is between the a and the b. Now it can't match any more as, but a* is also satisfied with an empty string. This means, after every single match you get another empty match.

We don't want that here, because it would introduce additional empty lines, so we discard those extraneous matches (which are at the beginnings of the lines, due to the right-to-left mode) by requiring that matches include the end of the line.

\$\endgroup\$
6
\$\begingroup\$

Haskell, 34 bytes

main=interact$concat.reverse.lines

[edit]

Saved one byte by replacing unlines with concat.

\$\endgroup\$
0
4
\$\begingroup\$

CJam, 7 bytes

qN/W%N*

Reads stdin, prints to stdout.

Explanation:

q       Get input.
N/      Split at newlines.
W%      Reverse list.
N*      Join with newlines.
\$\endgroup\$
4
\$\begingroup\$

Pyth, 5 bytes

jb_.z

This reverses the order of the lines with a simple split-reverse-join approach, but not quite like tac.

Try it online.

\$\endgroup\$
0
4
\$\begingroup\$

Befunge-93, 17 bytes

~:1+!#v_
>:#,_@>$

Nothing fancy here; just put everything on the stack, then pop it off.

\$\endgroup\$
4
\$\begingroup\$

Pure Bash (no external utilities), 56

mapfile a
for((i=${#a[@]};i--;));{
printf %s "${a[i]}"
}

This is one of the few answers to do exact tac emulation, as asked about in Dennis' comment:

$ echo -en 'a\nb' | ./tacemu.sh | xxd -g 1
0000000: 62 61 0a                                         ba.
$ echo -en 'a\nb\n' | ./tacemu.sh | xxd -g 1
0000000: 62 0a 61 0a                                      b.a.
$ 
\$\endgroup\$
1
  • \$\begingroup\$ Nice and inspiring. \$\endgroup\$
    – manatwork
    Commented Oct 1, 2015 at 16:07
4
\$\begingroup\$

Ruby, 16 bytes

puts [*$<].reverse
\$\endgroup\$
4
\$\begingroup\$

Python 2, 52 bytes

import sys;print''.join(sys.stdin.readlines()[::-1])
\$\endgroup\$
8
  • 1
    \$\begingroup\$ Doesn't input() read one line from stdin? \$\endgroup\$
    – lynn
    Commented Oct 1, 2015 at 9:39
  • \$\begingroup\$ @Mauris Edited it \$\endgroup\$
    – Beta Decay
    Commented Oct 1, 2015 at 12:22
  • \$\begingroup\$ What about import sys;print sys.stdin.read()[::-1] ? \$\endgroup\$
    – dieter
    Commented Oct 1, 2015 at 15:24
  • \$\begingroup\$ @dieter That reverses each character, the challenge asks for just the lines to be reversed \$\endgroup\$
    – Beta Decay
    Commented Oct 1, 2015 at 15:39
  • \$\begingroup\$ ok my bad - Didn't read it carefully, sorry \$\endgroup\$
    – dieter
    Commented Oct 1, 2015 at 16:12
3
\$\begingroup\$

sed, 9 bytes

1!G;h;$!d

No upvote wanted, this is a famous sed one-liner.

\$\endgroup\$
1
  • 10
    \$\begingroup\$ If it's not your own work, I suggest making your answer community wiki. \$\endgroup\$
    – lirtosiast
    Commented Oct 1, 2015 at 7:17
3
\$\begingroup\$

Perl, 16 bytes

print reverse<>
\$\endgroup\$
1
  • \$\begingroup\$ @Dennis oops, now reverted. \$\endgroup\$
    – steve
    Commented Oct 1, 2015 at 7:22
3
\$\begingroup\$

Powershell, 41 bytes

$a=$args|%{gc $_};[array]::Reverse($a);$a

Stores the content of a file line by line in a, reverses a and finally prints it.

\$\endgroup\$
3
\$\begingroup\$

GolfScript, 7 bytes

n/-1%n*

Online test here.

\$\endgroup\$
3
\$\begingroup\$

Burlesque, 6 bytes

ln<-uN

ln splits lines, <- reverses, uN joins lines and formats for raw output.

\$\endgroup\$
3
\$\begingroup\$

Bash, 48 43 characters

(Inspired by Digital Trauma's Bash answer. Upvotes for the idea should go to him.)

mapfile -c1 -C's=$2$s;set'
printf %s "$2$s"

Sample run:

bash-4.3$ echo -en 'a\nb' | bash tac.sh | xxd -g 1
0000000: 62 61 0a                                         ba.

bash-4.3$ echo -en 'a\nb\n' | bash tac.sh | xxd -g 1
0000000: 62 0a 61 0a                                      b.a.
\$\endgroup\$
2
  • \$\begingroup\$ I think you can do mapfile -c1 -Cf instead of mapfile -c1 -Cf a. \$\endgroup\$ Commented Oct 1, 2015 at 16:13
  • \$\begingroup\$ Correct. I also discovered it in meantime, just tried something around that tricky -C first. \$\endgroup\$
    – manatwork
    Commented Oct 1, 2015 at 16:21
3
\$\begingroup\$

JavaScript (SpiderMonkey Shell), 38 bytes

[...read(readline())].reverse().join``

Pretty simple


read() reads a file

readline() reads a string from STDIN

[...str] will split str into an array of chars

reverse() will reverse the array

join`` will collpase the array into a string

\$\endgroup\$
3
\$\begingroup\$

GNU Awk, 27 characters

(Inspired by Ed Morton's GNU Awk answer. CW as I not intended to hijack his solution.)

{s=$0RT s}END{printf"%s",s}

Note that by changing RTRS this becomes portable standard Awk but looses the ability to preserve the absence of the final newline.

Sample run:

bash-4.3$ echo -en 'a\nb' | awk '{s=$0RT s}END{printf"%s",s}' | xxd -g 1
0000000: 62 61 0a                                         ba.

bash-4.3$ echo -en 'a\nb\n' | awk '{s=$0RT s}END{printf"%s",s}' | xxd -g 1
0000000: 62 0a 61 0a                                      b.a.
\$\endgroup\$
2
  • \$\begingroup\$ You can remove the "%s" \$\endgroup\$
    – ninjalj
    Commented Oct 3, 2015 at 0:15
  • \$\begingroup\$ @ninjalj, only if we can assume that the input will never contain “%”. \$\endgroup\$
    – manatwork
    Commented Oct 3, 2015 at 11:14
3
\$\begingroup\$

C#, 179 171 bytes

using B=System.Console;class A{static void Main(){var a=new System.Collections.Stack();string b;while((b=B.ReadLine())!=null)a.Push(b);foreach(var c in a)B.WriteLine(c);}}

Reads lines, placing them in a stack, and then writes them backwards. I would use Mathematica for this, but it has no sense of EOF.

\$\endgroup\$
3
\$\begingroup\$

SNOBOL, 42 bytes

S S =INPUT CHAR(10) S :S(S)
 OUTPUT =S
END
\$\endgroup\$
2
\$\begingroup\$

Gema, 25 characters

*\n=@set{s;$0${s;}}
\Z=$s

Sample run:

bash-4.3$ echo -en 'a\nb' | gema '*\n=@set{s;$0${s;}};\Z=$s'
ba

bash-4.3$ echo -en 'a\nb\n' | gema '*\n=@set{s;$0${s;}};\Z=$s'
b
a
\$\endgroup\$
2
\$\begingroup\$

Hassium, 90 Bytes 86 Bytes

use IO;func main(){c=File.readLines(args[0]);for(x=c.length-1;x>=0; println(c[x--]))0;

See expanded here

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I you can shorten this a lot by abusing for syntax. See a sample here \$\endgroup\$ Commented Oct 1, 2015 at 14:50
  • \$\begingroup\$ Good call @FryAmTheEggman! I added it in. \$\endgroup\$ Commented Oct 1, 2015 at 15:04
2
\$\begingroup\$

sed, 7 bytes

G;h;$!d

This works for me (and it's the shortest solution elsewhere), but I don't really want to find out why. I just messed around with the famous 9-byte trick until I found this. I guess Ging the first line does nothing?

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Actually does something: your code produces an extra newline at the end of output. (G appends a newline and the content of hold space to the pattern space. While appending the content of empty hold space is indeed harmless, the newline is still appended.) \$\endgroup\$
    – manatwork
    Commented Oct 1, 2015 at 16:37
2
\$\begingroup\$

JavaScript(Node.js), 91 Bytes

console.log(require('fs').readFileSync(process.argv[2])+"".split(d="\n").reverse().join(d))
\$\endgroup\$
1
  • \$\begingroup\$ Did you mean console.log((require('fs').readFileSync(process.argv[2])+"").split(d="\n").reverse().join(d)) (92 bytes)? Your current code does not reverse the lines. \$\endgroup\$
    – Toothbrush
    Commented Oct 3, 2015 at 18:14
2
\$\begingroup\$

Bash + common utilities, 25

tr \\n ^G|rev|tr ^G \\n|rev

Here the ^G is a literal BEL character. I'm assuming the input is only printable ascii.

This transforms the entire input to one line by replacing newlines with BELs, then reverses that line, then transforms back to multiline, then reverses each line again, to get the desired output.

\$\endgroup\$
2
\$\begingroup\$

MATLAB, 44

@(x) strjoin(fliplr(strsplit(x,'\n')),'\n');

Splits the string at new lines, flips the resulting array, then rejoins with new line characters.

\$\endgroup\$
2
\$\begingroup\$

Julia, 65 bytes

open(s->print(join(reverse([l for l=readlines(s)]),"")),ARGS[1])

This takes a file as a command line argument and prints its lines in reverse order. Trailing newlines are moved to the front, unlike tac, which is legit.

Ungolfed:

function p(s::Stream)
    # Create a vector of the lines of the input stream
    L = [l for l in readlines(s)]

    # Reverse the vector and join it back into a string
    j = join(reverse(L), "")

    # Print the string to STDOUT
    print(j)
end

# Open the file specified in the first command line argument
# and apply the function p to its contents
open(p, ARGS[1])
\$\endgroup\$
2
\$\begingroup\$

Pip, 3 + 2 = 5 bytes

Uses the r and n flags; reads from stdin.

RVg

The r flag reads stdin and stores it as a list of lines in g (which is normally a list of command-line args). We then reverse that list, and it is auto-printed. The n flag causes lists to be output with newline as a separator.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.