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

我想反转文本文件(或标准输入)中的行顺序,保留每行的内容。

所以,即,从:

foobarbaz

我想以

bazbarfoo

有标准的UNIX命令行实用程序吗?

438867 次浏览

BSD尾部:

tail -r myfile.txt

参考:FreeBSDNetBSDOpenBSDosx手册页。

另外值得一提的是:taccat的反面)。coreutils的一部分。

将一个文件翻转到另一个文件

tac a.txt > b.txt

这是众所周知的Sed把戏

# reverse order of lines (emulates "tac")# bug/feature in HHsed v1.5 causes blank lines to be deletedsed '1!G;h;$!d'               # method 1sed -n '1!G;h;$p'             # method 2

(解释:前置非初始行以保存缓冲区,交换行并保存缓冲区,最后打印出行)

或者(更快的执行)从awk的单线

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

如果你不记得了,

perl -e 'print reverse <>'

在具有GNU实用程序的系统上,其他答案更简单,但并非全世界都是GNU/Linux。

尝试以下命令:

grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }"

如果你碰巧在vim使用

:g/^/m0

我真的很喜欢“尾巴-r”的答案,但我最喜欢的呆呆的答案是……

gawk '{ L[n++] = $0 }END { while(n--)print L[n] }' file
$ (tac 2> /dev/null || tail -r)

尝试tac,它适用于Linux,如果不起作用,请使用tail -r,它适用于BSD和OSX。

编辑下面生成一个从1到10的随机排序的数字列表:

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') **...**

其中点被替换为反转列表的实际命令

tac

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \<(tac)

Python:在sys.stdin上使用[::-1]

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]))")

只是Bash:)(4.0+)

function print_reversed {local lines ireadarray -t lines
for (( i = ${#lines[@]}; i--; )); doprintf '%s\n' "${lines[i]}"done}
print_reversed < file

最简单的方法是使用tac命令。taccat的倒数。示例:

$ cat order.txtroger shaharmin van buurenfpga vhdl arduino c++ java gridgain$ tac order.txt > inverted_file.txt$ cat inverted_file.txtfpga vhdl arduino c++ java gridgainarmin van buurenroger shah
sort -r < filename

rev < filename

最佳解决方案:

tail -n20 file.txt | tac

对于可能在外壳脚本中使用tac的跨操作系统(即OSX,Linux)解决方案,如上所述,使用自制,然后像这样使用别名tac:

安装lib

对于macOS

brew install coreutils

对于linux debian

sudo apt-get updatesudo apt-get install coreutils

然后添加别名

echo "alias tac='gtac'" >> ~/.bash_aliases (or wherever you load aliases)source ~/.bash_aliasestac myfile.txt
tac <file_name>

例子:

$ cat file1.txt12345
$ tac file1.txt54321

这将适用于BSD和GNU。

awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename

您可以使用vimstdinstdout。您也可以使用ex成为stdin0。vim只是ex的视觉模式。事实上,您可以将exvim -evim -E一起使用(改进的ex模式)。vim很有用,因为与sed这样的工具不同,它缓冲文件进行编辑,而sed用于流。您可能可以使用awk,但您必须手动缓冲变量中的所有内容。

我们的想法是做到以下几点:

  1. 从stdin读取
  2. 对于每一行,将其移动到第1行(反转)。命令是g/^/m0。这意味着全局,对于每一行g;匹配该行的开头,它匹配任何^;将其移动到地址0之后,即第1m0行。
  3. 打印所有内容。命令是%p。这意味着所有行%的范围;打印行p
  4. 强制退出而不保存文件。命令是q!。这意味着退出q;强行!
# Generate a newline delimited sequence of 1 to 10$ seq 1012345678910
# 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!'10987654321# 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/stdinseq 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

尾巴-r适用于大多数Linux和MacOS系统

seq 1 20|ail-r

在命令末尾放:| tac

TAC完全按照您的要求执行,它“将每个文件写入标准输出,最后一行第一”。

Tac与cat相反:-)。

对于Emacs用户:C-x h(选择整个文件),然后M-x reverse-region。也适用于仅选择部分或行并恢复它们。

如果要在适当位置修改文件,可以运行

sed -i '1!G;h;$!d' filename

这消除了创建临时文件然后删除或重命名原始文件的需要,结果相同。例如:

$tac file > file2$sed -i '1!G;h;$!d' file$diff file file2$

基于用表象回答,它几乎做了,但不完全,我想要的。

我看到了很多有趣的想法。但是试试我的想法。将您的文本插入以下内容:

rev|tr'\n' '~' | rev|tr '~' '\n'

它假设字符“~”不在文件中。这应该适用于追溯到1961年的每个UNIX shell。或者类似的东西。

我碰巧想获取一个非常大的文本文件有效的最后n行。

我尝试的第一件事是tail -n 10000000 file.txt > ans.txt,但我发现它非常慢,因为tail必须寻找位置,然后返回以打印结果。

当我意识到这一点时,我切换到另一个解决方案:tac file.txt | head -n 10000000 > ans.txt。这一次,寻道位置只需要从末端移动到所需的位置,节省50%的时间

带回家留言:

如果您的tail没有-r选项,请使用tac file.txt | head -n n

revtext here

rev <file>

rev texthere

您可以在命令行上使用Perl:

perl -e 'my @b=(); while(<>) {push(@b, $_);}; print join("", reverse(@b));' orig > rev