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 工具来做到这一点。
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 规范中,这是一个历史错误)。
使用 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 字符,因此这里有一个替代方案。这读取段落,替换~
换行符,然后随机播放,然后~
在显示结果之前将其转换回换行符:
$ 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)
如果您的文本可能包含~
,则使用文本不包含的另一个字符作为临时行分隔符。
使用 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)