我有一个文本文件,其中有分隔文本块的空行。我想使用 *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";