多行文件随机播放

多行文件随机播放

我有一个文本文件,其中有分隔文本块的空行。我想使用 *NIX 命令行工具来打乱此文件,同时尊重块结构。换句话说,在输出中我想看到块的顺序发生了变化;块内的行及其顺序保持不变。

输入文件示例:

line 1
line 2

line 10
line 20
line 30

line 100
line 200

输出文件(随机播放后):

line 10
line 20
line 30

line 1
line 2

line 100
line 200

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

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

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

答案1

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-

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

人们可能需要通过管道来sed '$d'删除尾随的空白行。

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

答案2

使用 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

不使用 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

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

答案3

使用perl:

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

或者作为脚本文件展开:

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

相关内容