多行文件洗牌

Vla*_*ecs 5 grep sed awk text-processing

我有一个文本文件,其中有空行分隔文本块。我想使用 *NIX 命令行工具在尊重块结构的同时打乱这个文件。换句话说,在输出中,我希望看到块的更改顺序;块内的行和它们的顺序保持不变。

输入文件示例:

line 1
line 2

line 10
line 20
line 30

line 100
line 200
Run Code Online (Sandbox Code Playgroud)

输出文件(混洗后):

line 10
line 20
line 30

line 1
line 2

line 100
line 200
Run Code Online (Sandbox Code Playgroud)

当然,重复运行应该给出不同的块顺序。

文件的第一行总是非空的。没有双空行。文件的最后一行始终为空。

我编写了一个非常简单的 Python 脚本,它读取列表列表中的所有行,对其进行打乱并输出。我很好奇我是否可以使用标准的 *NIX 工具来做到这一点。

Sté*_*las 8

POSIXly,您可以执行以下操作:

<file awk '
  BEGIN{srand(); n=rand()}
  {print n, NR, $0}
  !NF {n=rand()}
  END {if (NF) print n, NR+1, ""}' |
  sort -nk1 -k2 |
  cut -d' ' -f3-
Run Code Online (Sandbox Code Playgroud)

也就是说,在每一行<a-random-number-that-changes-with-each-paragraph>前面加上行号,然后按第一个数字和第二个数字排序,以保持段落中的行顺序并删除那些额外的数字。

一个人可能想要通过管道来sed '$d'删除尾随的空行。

请注意,大多数awk实现 srand()使用 unix 纪元时间来为伪随机数生成器提供种子,因此如果在同一秒内运行两次,您可能会得到相同的结果(尽管我很遗憾,但现在已刻在 POSIX 规范中,这是一个历史错误)。


Joh*_*024 5

使用 GNU 工具,这将段落分成 NUL 分隔的组,将它们打乱,然后删除 NUL:

$ sed '1s/^/\n/; s/^$/\x00/' input | shuf -z | sed '1d; s/\x00//'
line 100
line 200

line 10
line 20
line 30

line 1
line 2
Run Code Online (Sandbox Code Playgroud)

不使用 NUL 的替代方法

由于并非所有工具都支持 NUL 字符,因此这里有一个替代方案。这读取段落,替换~换行符,然后随机播放,然后~在显示结果之前将其转换回换行符:

$ awk '{gsub(/\n/, "~")} 1' RS= input | shuf | awk '{gsub(/~/, "\n")} 1' ORS="\n\n"
line 10
line 20
line 30

line 100
line 200

line 1
line 2
Run Code Online (Sandbox Code Playgroud)

如果您的文本可能包含~,则使用文本不包含的另一个字符作为临时行分隔符。


Jef*_*ler 5

使用 perl:

perl -MList::Util -00 -e 'chomp(my @a=<>); print join("\n\n", List::Util::shuffle @a) . "\n";' < input
Run Code Online (Sandbox Code Playgroud)

或者作为脚本文件展开:

#!/usr/bin/perl
use List::Util 'shuffle';
local $/ = "";  ## paragraph mode
chomp(my @a = <>);
print join("\n\n", shuffle @a) . "\n";
Run Code Online (Sandbox Code Playgroud)