如何倒转文件中行的顺序?

835

我想要反转一个文本文件(或stdin)中的行顺序,同时保留每行的内容。

也就是说,例如,从以下内容开始:

foo
bar
baz

我想最终拥有

baz
bar
foo

有没有标准的UNIX命令行实用程序可以做到这一点?


8
关于反转行的重要说明:首先要确保你的文件有一个尾随的换行符。否则,在输出文件中,输入文件的最后两行将合并为一行(至少使用 perl -e 'print reverse <>',但可能也适用于其他方法)。 - jakub.g
1
可能是如何反转文本文件的行?的重复问题。 - Greg Hewgill
这也几乎是一个重复的问题(尽管更旧),与http://unix.stackexchange.com/questions/9356/how-can-i-print-lines-from-file-backwards-without-using-tac相同。与那种情况一样,迁移到unix.stackexchange.com可能是适当的。 - mc0e
24个回答

-1

你可以使用 vimstdinstdout 来完成。你也可以使用 ex 来符合 POSIX 标准。实际上,vim 只是 ex 的可视化模式。事实上,你可以使用 vim -evim -E(改进的 ex 模式)来使用 ex

vim 很有用,因为与像 sed 这样的工具不同,它会缓冲文件以进行编辑,而 sed 用于流。你可能可以使用 awk,但你需要手动将所有内容缓冲到变量中。

思路如下:

  1. 从标准输入读取
  2. 对于每一行,将其移动到第一行(以进行反转)。命令为g/^/m0。这意味着全局地,对于每一行g;匹配行的开头,即匹配任何内容^;将其移动到地址0之后,即第一行m0
  3. 打印所有内容。命令为%p。这意味着对于所有行的范围%;打印该行p
  4. 强制退出而不保存文件。命令为q!。这意味着退出q;强制执行!
# 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

如何使其可重复使用

我使用一个名为ved的脚本(类似于vim编辑器的sed)来使用vim编辑stdin。将此添加到名为ved的文件中,并将其放在您的路径中:

#!/usr/bin/env sh

vim - --not-a-term -Es "$@" +'%p | q!'

我使用一个+命令代替+'%p' +'q!',因为vim限制你最多只能使用10个命令。所以将它们合并允许"$@"有9个+命令而不是8个。

然后你可以这样做:

seq 10 | ved +'g/^/m0'

如果您没有vim 8,请将其放入ved中:

#!/usr/bin/env sh

vim -E "$@" +'%p | q!' /dev/stdin

-2

不确定是否遗漏了什么。 管道排序怎么样

例如 cat 文件 | sort -r

如果我错过了这个问题的要点,对不起。 我经常使用它来扫描系统日志。

在Linux中: watch " tail /var/log/syslog | sort -r "

希望能帮助到某些人 最好的祝愿


1
那会对事情进行排序,但这并不是意图。 - Pavel Gurkov

-7

tail -r 在大多数 Linux 和 MacOS 系统中都可以使用

seq 1 20 | tail -r


这个重复了2009年被接受的回答。 - tripleee

-10
sort -r < filename

或者

rev < filename

7
-r 参数只有在输入已经按顺序排序的情况下才能发挥作用,但这里并非如此。rev 命令可以反转每行的字符顺序,但会保持行的顺序不变,这也不是 Scotty 所要求的。因此,这个答案实际上并没有回答 Scotty 的问题。 - Alexander Stumpf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接