I'd like to reverse the order of lines in a text file (or stdin), preserving the contents of each line.
So, i.e., starting with:
foo
bar
baz
I'd like to end up with
baz
bar
foo
Is there a standard UNIX commandline utility for this?
I'd like to reverse the order of lines in a text file (or stdin), preserving the contents of each line.
So, i.e., starting with:
foo
bar
baz
I'd like to end up with
baz
bar
foo
Is there a standard UNIX commandline utility for this?
tac text.txt > text.txt
it wipes out the files? –
Asafetida -r
option of tail –
Frown tail -r
, it seems like the original last line is being concatenated with the original second to last line to form the new first line. Probably because the text stream doesn't have a newline at the end? –
Soinski tail -r blah > blah
empties the file. Unlike tac
below. –
Cayes Also worth mentioning: tac
(the, ahem, reverse of cat
). Part of coreutils.
tac a.txt > b.txt
brew install coreutils
(installs as gtac
by default). –
Behnken tac a.txt > a.txt
I don't want a new file, just want the current one flipped. –
Rizas echo -n "abc\ndee" > test; tac test
. –
Asafetida ed/sed
–
Liris cat a.txt | tac >b.txt
works! –
Inlaid tac file | sponge file
if you want to change the file and don't want both versions. –
Subtile ggVG
) and 2. run the command :!tac
–
Embowed tac text.txt > text.txt
it wipes out the files? –
Asafetida -r
option of tail –
Frown tail -r
, it seems like the original last line is being concatenated with the original second to last line to form the new first line. Probably because the text stream doesn't have a newline at the end? –
Soinski tail -r blah > blah
empties the file. Unlike tac
below. –
Cayes There's the well-known sed tricks:
# reverse order of lines (emulates "tac")
# bug/feature in HHsed v1.5 causes blank lines to be deleted
sed '1!G;h;$!d' # method 1
sed -n '1!G;h;$p' # method 2
(Explanation: prepend non-initial line to hold buffer, swap line and hold buffer, print out line at end)
Alternatively (with faster execution) from the awk one-liners:
awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file*
If you can't remember that,
perl -e 'print reverse <>'
On a system with GNU utilities, the other answers are simpler, but not all the world is GNU/Linux...
tail -r
and tac. –
Zulemazullo tac
is expected to handle arbitrary large files that do not fit in memory (line length is still limited though). It is unclear whether sed
solution works for such files. –
Marginalia print
just prints to STDOUT
so you can easily pipe the output or write it to a new file. Of course its worth mentioning that you will be placing the entire file in memory with this version. –
Chevy -i
without making a temporary file. –
Karaganda sed
version. At least I can see that for perl
and awk
(and other answers but I don't want to download something and am not on Linux) –
Lashawn at the end of your command put:
| tac
tac does exactly what you're asking for, it "Write each FILE to standard output, last line first."
tac is the opposite of cat :-).
tac
command, this is useful for new users who might end up searching the same subject. –
Photomural tac filename.ext
(reverse of cat filename.ext
) –
Innocent If you happen to be in vim
use
:g/^/m0
Explanation from @Ronopolis below:
g
means "do this globally.
^
means "the beginning of a line".
m
means "move the line to a new line number.0
is which line to move to.
0
means "top of the file, before the current line 1".
So: "Find every line that has a beginning, and move it to line number 0."
You find line 1, and move it to the top. Does nothing. Then find line 2 and move it above line 1, to the top of the file. Now find line 3 and move it to the top. Repeat this for every line. At the end you finish by moving the last line to the top. When you are done, you've reversed all the lines.
tac <file_name>
example:
$ cat file1.txt
1
2
3
4
5
$ tac file1.txt
5
4
3
2
1
$ (tac 2> /dev/null || tail -r)
Try tac
, which works on Linux, and if that doesn't work use tail -r
, which works on BSD and OSX.
tac myfile.txt
- what am I missing? –
Ammonium tail -r
in case tac
is not available. tac
is not POSIX compliant. Neither is tail -r
. Still not foolproof, but this improves the odds of things working. –
Philae tac
is available, but runs out of RAM and swap half way through consuming a giant input stream. It fails, and then tail -r
succeeds in processing the remainder of the stream giving an incorrect result. –
Joly brew install coreutils
and use gtac
in place of tac
and if your prefer add tac as an alias to gtac
if for example you wanted a shell script that used it cross platform (Linux, OSX) –
Henghold Try the following command:
grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }"
sed 's/^[0-9]*://g'
–
Disengagement nl
by default will fail to number empty lines. The -ba
option is available on some systems, not is not universal (HP/UX comes to mind, though I wish it wouldn't) whereas grep -n
will always number every line that matches the (in this case empty) regex. –
Portillo cut -d: -f2-
–
Infeasible Just Bash :) (4.0+)
function print_reversed {
local lines i
readarray -t lines
for (( i = ${#lines[@]}; i--; )); do
printf '%s\n' "${lines[i]}"
done
}
print_reversed < file
-nenenenenenene
and witness the reason why people recommend always using printf '%s\n'
instead of echo
. –
Leffen For cross OS (i.e. OSX, Linux) solution that may use tac
inside a shell script use homebrew as others have mentioned above, then just alias tac like so:
Install lib
For MacOS
brew install coreutils
For linux debian
sudo apt-get update
sudo apt-get install coreutils
Then add alias
echo "alias tac='gtac'" >> ~/.bash_aliases (or wherever you load aliases)
source ~/.bash_aliases
tac myfile.txt
I really like the "tail -r" answer, but my favorite gawk answer is....
gawk '{ L[n++] = $0 }
END { while(n--)
print L[n] }' file
mawk
on Ubuntu 14.04 LTS - works, so it is not GNU awk specific. +1 –
Zealotry n++
can be replaced with NR
–
Stardom The simplest method is using the tac
command. tac
is cat
's inverse.
Example:
$ cat order.txt
roger shah
armin van buuren
fpga vhdl arduino c++ java gridgain
$ tac order.txt > inverted_file.txt
$ cat inverted_file.txt
fpga vhdl arduino c++ java gridgain
armin van buuren
roger shah
If you want to modify the file in place, you can run
sed -i '1!G;h;$!d' filename
This removes the need to create a temporary file and then delete or rename the original and has the same result. For example:
$tac file > file2
$sed -i '1!G;h;$!d' file
$diff file file2
$
Based on the answer by ephemient, which did almost, but not quite, what I wanted.
EDIT the following generates a randomly sorted list of numbers from 1 to 10:
seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') **...**
where dots are replaced with actual command which reverses the list
tac
seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \
<(tac)
python: using [::-1] on sys.stdin
seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \
<(python -c "import sys; print(''.join(([line for line in sys.stdin])[::-1]))")
This will work on both BSD and GNU.
awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename
I see lots of interesting ideas. But try my idea. Pipe your text into this:
rev | tr '\n' '~' | rev | tr '~' '\n'
which assumes that the character '~' is not in the file. This should work on every UNIX shell going back to 1961. Or something like that.
cat foo.txt | rev | tr '\n' '~' | rev | tr '~' '\n' > bar.txt
–
Pressroom For Emacs users: C-x h
(select the whole file) and then M-x reverse-region
. Also works for only selecting parts or the lines and reverting those.
It happens to me that I want to get the last n
lines of a very large text file efficiently.
The first thing I tried is tail -n 10000000 file.txt > ans.txt
, but I found it very slow, for tail
has to seek to the location and then moves back to print the results.
When I realize it, I switch to another solution: tac file.txt | head -n 10000000 > ans.txt
. This time, the seek position just needs to move from the end to the desired location and it saves 50% time!
Take home message:
Use tac file.txt | head -n n
if your tail
does not have the -r
option.
You may use Perl on the commandline:
perl -e 'my @b=(); while(<>) {push(@b, $_);}; print join("", reverse(@b));' orig > rev
Best solution:
tail -n20 file.txt | tac
man tail
, man tac
at some point rather than being spoonfed. +1 –
Angola You can do it with vim
stdin
and stdout
. You can also use ex
to be POSIX compliant. vim
is just the visual mode for ex
. In fact, you can use ex
with vim -e
or vim -E
(improved ex
mode).
vim
is useful because unlike tools like sed
it buffers the file for editing, while sed
is used for streams. You might be able to use awk
, but you would have to manually buffer everything in a variable.
The idea is to do the following:
g/^/m0
. This means globally, for each line g
; match the start of the line, which matches anything ^
; move it after address 0, which is line 1 m0
.%p
. This means for the range of all lines %
; print the line p
.q!
. This means quit q
; forcefully !
.# Generate a newline delimited sequence of 1 to 10
$ seq 10
1
2
3
4
5
6
7
8
9
10
# Use - to read from stdin.
# vim has a delay and annoying 'Vim: Reading from stdin...' output
# if you use - to read from stdin. Use --not-a-term to hide output.
# --not-a-term requires vim 8.0.1308 (Nov 2017)
# Use -E for improved ex mode. -e would work here too since I'm not
# using any improved ex mode features.
# each of the commands I explained above are specified with a + sign
# and are run sequentially.
$ seq 10 | vim - --not-a-term -Es +'g/^/m0' +'%p' +'q!'
10
9
8
7
6
5
4
3
2
1
# non improved ex mode works here too, -e.
$ seq 10 | vim - --not-a-term -es +'g/^/m0' +'%p' +'q!'
# If you don't have --not-a-term, use /dev/stdin
seq 10 | vim -E +'g/^/m0' +'%p' +'q!' /dev/stdin
# POSIX compliant (maybe)
# POSIX compliant ex doesn't allow using + sign to specify commands.
# It also might not allow running multiple commands sequentially.
# The docs say "Implementations may support more than a single -c"
# If yours does support multiple -c
$ seq 10 | ex -c "execute -c 'g/^/m0' -c '%p' -c 'q!' /dev/stdin
# If not, you can chain them with the bar, |. This is same as shell
# piping. It's more like shell semi-colon, ;.
# The g command consumes the |, so you can use execute to prevent that.
# Not sure if execute and | is POSIX compliant.
seq 10 | ex -c "execute 'g/^/m0' | %p | q!" /dev/stdin
How to make this reusable
I use a script I call ved
(vim editor like sed
) to use vim to edit stdin
. Add this to a file called ved
in your path:
#!/usr/bin/env sh
vim - --not-a-term -Es "$@" +'%p | q!'
I am using one +
command instead of +'%p' +'q!'
, because vim limits you to 10 commands. So merging them allows the "$@"
to have 9 +
commands instead of 8.
Then you can do:
seq 10 | ved +'g/^/m0'
If you don't have vim 8, put this in ved
instead:
#!/usr/bin/env sh
vim -E "$@" +'%p | q!' /dev/stdin
Not sure if I missed something. How about pipe into sort
i.e. cat file | sort -r
Sorry if I missed the point of this question. I often use this to scan syslogs.
In linux : watch " tail /var/log/syslog | sort -r "
Hope it helps someone Best wishes
© 2022 - 2024 — McMap. All rights reserved.
perl -e 'print reverse <>'
but it probably applies to other methods too). – Prosecute