Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have a simple question (I think). I want to print lines from a file backwards without using tac command. Is there any other solution to do such thing with bash?

share|improve this question

migrated from stackoverflow.com Mar 16 '11 at 13:15

This question came from our site for professional and enthusiast programmers.

6 Answers 6

Using sed to emulate tac:

sed '1!G;h;$!d' ${inputfile}
share|improve this answer
    
This is a good solution, but some explanation for how this works will be even better. –  Chen Levy Mar 16 '11 at 11:19
8  
It's a famous sed one-liner. See "36. Reverse order of lines (emulate "tac" Unix command)." in Famous Sed One-Liners Explained for a full explanation of how it works. –  Johnsyweb Mar 16 '11 at 11:37
5  
Perhaps worth noting: "These two one-liners actually use a lot of memory because they keep the whole file in hold buffer in reverse order before printing it out. Avoid these one-liners for large files." –  Mr. Shickadance Mar 17 '11 at 14:33
    
So do all the other answers (except maybe the one using sort - there's a chance it will use a temporary file). –  Random832 Apr 15 '11 at 20:39
1  
Note that tac is faster for regular files because it reads the file backward. For pipes, it has to do the same as the other solutions (hold in memory or in temp files), so is not significantly faster. –  Stéphane Chazelas Sep 14 '12 at 5:48

As you asked to do it in bash, here is a solution that doesn't make use of awk, sed or perl, just a bash function:

reverse ()
{
    local line
    if IFS= read -r line
    then
        reverse
        printf '%s\n' "$line"
    fi
}

The output of

echo 'a
b
c
d
' | reverse

is

d
c
b
a

As expected.

But beware that lines are stored in memory, one line in each recursively called instance of the function. So careful with big files.

share|improve this answer
1  
It quickly becomes impractically slow as file size increases, compared to even the slowest of the other answers, and as you have suggested, it blows memory pretty easily: *bash recursive function... but its an interesting idea. Segmentation fault * –  Peter.O Aug 6 '12 at 16:55

You can pipe it through:

awk '{print NR" "$0}' | sort -k1 -n -r | sed 's/^[^ ]* //g'

The awk prefixes each line with the line number followed by a space. The sort reverses the order of the lines by sorting on the first field (line number) in reverse order, numeric style. And the sed strips off the line numbers.

The following example shows this in action:

pax$ echo 'a
b
c
d
e
f
g
h
i
j
k
l' | awk '{print NR" "$0}' | sort -k1 -n -r | sed 's/^[^ ]* //g'

It outputs:

l
k
j
i
h
g
f
e
d
c
b
a
share|improve this answer
    
Ok. Thanks! I'll try this. And thanks for the comments! –  Ionut Ungureanu Mar 16 '11 at 11:04
5  
cat -n acts much like awk '{print NR" "$0}' –  Chen Levy Mar 16 '11 at 11:09
1  
I think that's called Schwartzian transform, or decorate-sort-undecorate –  ninjalj Mar 17 '11 at 23:25

In perl:

cat | perl -e 'while(<>) { push @a, $_; } foreach (reverse(@a)) { print; }'

share|improve this answer
1  
or the shorter perl -e 'print reverse<>' –  ninjalj Mar 17 '11 at 23:21
1  
(Actually, there's a shorter one, but it's really ugly, witness its awfulness: perl -pe '$\=$_.$\}{' ) –  ninjalj Mar 17 '11 at 23:21
    
@Frederik Deweerdt: Fast, but it loses the first line... @ ninjalj: reverse<) is fast: good! but the "really ugly" one is extremely slow as the number of lines increases.!!.... –  Peter.O Aug 6 '12 at 16:41
    
@Peter.O the -n was superfluous there, thanks. –  Frederik Deweerdt Aug 17 '12 at 23:46

awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file.txt

via awk one liners

share|improve this answer
2  
A shorter one: awk 'a=$0RS a{}END{printf a}' –  ninjalj Mar 17 '11 at 23:34
    
@ninjalj: it may be shorter, but it becomes extremely slow as the file size gets larger. I gave up after waiting for 2min 30sec. but your first perl reverse<>` it the best/fastest answer on the page (to me), at 10 times faster than this awk answer (all the awk anseres are about the same, time-wise) –  Peter.O Aug 6 '12 at 17:26
1  
Or awk '{a[NR]=$0} END {while (NR) print a[NR--]}' –  Stéphane Chazelas Sep 14 '12 at 5:41
awk '{print NR" "$0}' | sort -nr
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.