ChatGPT解决这个技术问题 Extra ChatGPT

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

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

所以,即,从:

foo
bar
baz

我想结束

baz
bar
foo

是否有一个标准的 UNIX 命令行实用程序来解决这个问题?

关于反转行的重要说明:首先确保您的文件有一个尾随换行符。否则,输入文件的最后两行将合并为输出文件中的一行(至少使用 perl -e 'print reverse <>',但它可能也适用于其他方法)。
也几乎是 unix.stackexchange.com/questions/9356/… 的副本(尽管较旧)。在这种情况下,迁移到 unix.stackexchange.com 可能是合适的。

r
rodrigo-silveira

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

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

tac a.txt > b.txt

特别值得一提的是那些使用没有 -r 选项的 tail 版本的人! (大多数 Linux 人都有 GNU tail,它没有 -r,所以我们有 GNU tac)。
只是一个注释,因为人们之前提到过 tac,但 tac 似乎没有安装在 OS X 上。并不是说用 Perl 编写替代品很难,但我没有真正的替代品。
您可以从 Fink 获得适用于 OS X 的 GNU tac。您可能还希望获得 GNU tail,因为它做了一些 BSD tail 没有做的事情。
如果您将 OS X 与 homebrew 一起使用,您可以使用 brew install coreutils 安装 tac(默认安装为 gtac)。
问题之一是如果文件没有尾随新行,则前 2 行可能会合并为 1 行。 echo -n "abc\ndee" > test; tac test
C
Cristian Ciupitu

BSD尾巴:

tail -r myfile.txt

参考:FreeBSDNetBSDOpenBSDOS X 手册页。


请记住,“-r”选项不符合 POSIX。下面的 sed 和 awk 解决方案即使在最不稳定的系统中也能正常工作。
刚刚在 Ubuntu 12.04 上尝试过,发现我的 tail (8.13) 版本没有 -r 选项。改用'tac'(见下面Mihai的回答)。
复选标记应在下方移动到 tac。 tail -r 在 Ubuntu 12/13、Fedora 20、Suse 11 上失败。
tail -r ~/1 ~ tail: 无效选项 -- r 尝试 `tail --help' 获取更多信息。看起来像它的新选择
答案当然应该提到这仅适用于 BSD,特别是因为 OP 要求提供“标准 UNIX”实用程序。这不在 GNU tail 中,所以它甚至不是事实上的标准。
i
iolsmit

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

(说明:在非初始行前添加保存缓冲区,交换行和保存缓冲区,在末尾打印行)

或者(执行速度更快)from the awk one-liners

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

如果你记不住,

perl -e 'print reverse <>'

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


来自同一来源: awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file* sed 和 awk 版本都适用我的busybox路由器。 'tac' 和 'tail -r' 没有。
我希望这是公认的答案。因为 sed 始终可用,但 tail -r 和 tac 不可用。
@ryenus:tac 有望处理不适合内存的任意大文件(尽管行长度仍然有限)。目前尚不清楚 sed 解决方案是否适用于此类文件。
唯一的问题是:准备等待:-)
更准确地说:sed 代码在 O(n^2) 中,对于大文件可能非常慢。因此,我赞成 awk 替代方案,线性。我没有尝试 perl 选项,对管道不太友好。
D
Dror

在你的命令末尾放:| tac

tac 完全符合您的要求,它“将每个文件写入标准输出,最后一行先。”

tac 是 cat 的对立面 :-)。


他为什么要?请解释 tac 命令的值,这对可能最终搜索同一主题的新用户很有用。
这确实应该是公认的答案。可惜上面有这么多票。
顺便说一句:如果它来自文件,您不必通过管道传输到 tac。您可以简单地tac filename.extcat filename.ext 的反转)
D
DerMike

如果您碰巧在 vim 使用

:g/^/m0

如果您简要解释一下它的作用,我会投赞成票。
是的,我明白了,但我的意思是分解 vim 命令的各个部分正在做什么。我现在查看了@kenorb 链接的答案,它提供了解释。
表示“全局执行此操作。^ 表示“行的开头”。m 表示“将行移动到新的行号。 0 是要移动到哪一行。 0 表示“文件顶部,在当前行 1 之前”。所以:“找到每一行都有一个开头,并将其移动到第 0 行。”您找到第 1 行,并将其移至顶部。什么也没做。然后找到第 2 行并将其移到第 1 行上方,到文件的顶部。现在找到第 3 行并将其移到顶部。对每一行重复此操作。最后,您通过将最后一行移到顶部来完成。完成后,您已经反转了所有行。
应该注意的是, :g 全局命令的行为方式非常特殊,而不是简单地使用范围。例如,命令 ":%m0" 不会颠倒行的顺序,而 ":%normal ddggP" 会(就像 ":g/^/normal ddggP" 一样)。不错的技巧和解释...哦,是的,忘记了令牌“请参阅:帮助:g以获取更多信息”...
D
Daniel Alder
tac <file_name>

例子:

$ cat file1.txt
1
2
3
4
5

$ tac file1.txt
5
4
3
2
1

D
DigitalRoss
$ (tac 2> /dev/null || tail -r)

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


为什么不tac myfile.txt - 我错过了什么?
@sage,在 tac 不可用的情况下回退到 tail -rtac 不符合 POSIX。 tail -r 也不是。仍然不是万无一失,但这提高了工作的可能性。
我明白了-例如,当命令失败时您无法手动/交互地更改命令。对我来说已经足够好了。
您需要进行适当的测试以查看 tac 是否可用。如果 tac 可用,但内存不足并在消耗大量输入流的过程中进行交换,会发生什么情况。它失败了,然后 tail -r 成功地处理了流的其余部分,给出了不正确的结果。
@PetrPeller 请参阅 Robert 以上关于 OSX 使用自制软件的评论的回答。 brew install coreutils 并使用 gtac 代替 tac,如果您更喜欢将 tac 添加为 gtac 的别名,例如您想要一个跨平台(Linux、OSX)使用它的 shell 脚本
k
kenorb

尝试以下命令:

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

而不是 gawk 声明,我会做这样的事情:sed 's/^[0-9]*://g'
为什么不使用“nl”而不是 grep -n ?
@GoodPerson,默认情况下 nl 将无法为空行编号。 -ba 选项在某些系统上可用,但不是通用的(想到 HP/UX,但我希望它不会),而 grep -n 将始终编号 每一 行匹配(在这种情况下为空)正则表达式。
我使用 cut -d: -f2- 而不是 gawk
k
konsolebox

只是重击 :) (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

+1 用于 bash 和 O(n) 中的答案以及不使用递归(如果可以,+3)
尝试使用包含行 -nenenenenenene 的文件,并见证人们建议始终使用 printf '%s\n' 而不是 echo 的原因。
@mtraceur 这次我同意这一点,因为这是一个通用功能。
l
lacostenycoder

对于可能在 shell 脚本中使用 tac 的跨操作系统(即 OSX、Linux)解决方案,请使用上面其他人提到的自制软件,然后像这样使用别名 tac:

安装库

对于 MacOS

brew install coreutils

对于 linux debian

sudo apt-get update
sudo apt-get install coreutils 

然后添加别名

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

K
Kalle Richter

最简单的方法是使用 tac 命令。 taccat 的逆。例子:

$ 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 

不知道为什么这个答案出现在下面的答案之前,但它是 stackoverflow.com/a/742485/1174784 的欺骗 - 这是几年前发布的。
V
Vikdor

我真的很喜欢“tail -r”的答案,但我最喜欢的 gawk 答案是......

gawk '{ L[n++] = $0 } 
  END { while(n--) 
        print L[n] }' file

在 Ubuntu 14.04 LTS 上使用 mawk 测试 - 有效,因此它不是 GNU awk 特定的。 +1
n++ 可以替换为 NR
H
HoldOffHunger

如果要就地修改文件,可以运行

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

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

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

基于 answer by ephemient,它几乎但不完全是我想要的。


Y
Yauhen Yakimovich

编辑以下生成从 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]))")

V
Vadim Kotov

这将适用于 BSD 和 GNU。

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

d
driver

我看到了很多有趣的想法。但是试试我的想法。将您的文本输入:

转 | tr '\n' '~' |转 | tr '~' '\n'

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


谢谢,这在我的 MacOS 上效果很好。 cat foo.txt | rev | tr '\n' '~' | rev | tr '~' '\n' > bar.txt
M
Marius Hofert

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


y
youkaichao

我碰巧想有效地获取一个非常大的文本文件的最后 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


G
Georg Fischer

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

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


P
Pang

最佳解决方案:

tail -n20 file.txt | tac

欢迎来到堆栈溢出!虽然此代码段可能会解决问题,但 including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性注释来挤满你的代码,这会降低代码和解释的可读性!
d
dosentmatter

您可以使用 vim stdinstdout 来完成。您也可以使用 ex 作为 POSIX compliantvim 只是 ex 的可视模式。事实上,您可以将 exvim -evim -E 一起使用(改进的 ex 模式)。 vim 很有用,因为与 sed 之类的工具不同,它缓冲文件以供编辑,而 sed 用于流。您也许可以使用 awk,但您必须手动缓冲变量中的所有内容。

想法是执行以下操作:

从标准输入读取每行将其移动到第 1 行(反转)。命令是 g/^/m0。这意味着全局,对于每一行 g;匹配行首,匹配任何内容 ^;将其移到地址 0 之后,即第 1 行 m0。打印一切。命令是 %p。这意味着对于所有行的范围 %;打印第 p 行。强制退出而不保存文件。命令是 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 的脚本(类似 sed 的 vim 编辑器)使用 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

P
Pmpr.ir
rev
text here

或者

rev <file>

或者

rev texthere

您好,欢迎来到 Stack Overflow!当你回答一个问题时,你应该包括某种解释,比如作者做错了什么以及你做了什么来解决它。我告诉你这个是因为你的答案被标记为低质量,目前正在审查中。您可以通过点击“编辑”按钮来edit您的答案。
特别是。旧的、已得到很好回答的问题的新答案需要充分的理由来添加另一个答案。
rev 也会水平翻转文本,这不是所需的行为。
B
Bohdan

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

序列 1 20 |尾 -r


这重复了 2009 年接受的答案。
P
Paul Roub
sort -r < filename

或者

rev < filename

sort -r 仅在输入已排序时才有效,此处并非如此。 rev 反转每行的字符,但保持行顺序不变,这也不是 Scotty 所要求的。所以这个答案实际上根本就没有答案。