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?
This question is related to
shell
unix
command-line
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]))")
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.
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...
rev
text here
or
rev <file>
or
rev texthere
I had the same question, but I also wanted the first line (header) to stay on top. So I needed to use the power of awk
cat dax-weekly.csv | awk '1 { last = NR; line[last] = $0; } END { print line[1]; for (i = last; i > 1; i--) { print line[i]; } }'
PS also works in cygwin or gitbash
sort -r < filename
or
rev < filename
I really like the "tail -r" answer, but my favorite gawk answer is....
gawk '{ L[n++] = $0 }
END { while(n--)
print L[n] }' file
This will work on both BSD and GNU.
awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename
$ (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.
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 :-).
tail -r works in most Linux and MacOS systems
seq 1 20 | tail -r
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.
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.
Try the following command:
grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }"
If you happen to be in vim
use
:g/^/m0
You may use Perl on the commandline:
perl -e 'my @b=(); while(<>) {push(@b, $_);}; print join("", reverse(@b));' orig > rev
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
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
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.
Best solution:
tail -n20 file.txt | tac
tac <file_name>
example:
$ cat file1.txt
1
2
3
4
5
$ tac file1.txt
5
4
3
2
1
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
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
Source: Stackoverflow.com