bash - 将一个太大而无法容纳在内存中的文件洗牌

时间:2016-11-26 04:02:16

标签: bash

我的文件太大而无法放入内存中。 shuf似乎在RAM中运行,并且sort -R没有随机播放(相同的行最终彼此相邻;我需要对所有行进行混洗)。除了推出自己的解决方案之外还有其他选择吗?

6 个答案:

答案 0 :(得分:8)

使用decorate-sort-undecorate模式和$ seq 10 | awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8- 8 5 1 9 6 3 7 2 10 4 形式,您可以执行以下操作:

$ awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' SORTED.TXT | sort -n | cut -c8- > SHUFFLED.TXT

对于文件,您可以这样做:

cat

000000管道开头的文件。

这可以通过生成999999sort之间的随机数列(装饰)来实现;对该列进行排序(排序);然后删除列(undecorate)。这应该适用于sort不能理解数字的平台,方法是生成一个带有前导零的列用于词典排序。

如果需要,您可以通过多种方式增加随机化:

  1. 如果您的平台的awk 'BEGIN{srand();} {printf "%0.15f\t%s\n", rand(), $0;}' FILE.TXT | sort -n | cut -f 2-理解数值(POSIX,GNU和BSD),您可以rand使用近似双浮点进行随机表示。

  2. 如果您仅限于词典排序,只需将对awk 'BEGIN{srand();} {printf "%06d%06d\t%s\n", rand()*1000000,rand()*1000000, $0;}' FILE.TXT | sort -n | cut -f 2-的两次调用合并为一列,如下所示:var json = [{cover:"https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Internet2.jpg/440px-Internet2.jpg", album:"test1"},{cover:"https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Internet2.jpg/440px-Internet2.jpg", album:"test2"}]; for (var x = 0; x<json.length;x++){ var fig = document.createElement("figure"); var figCap = document.createElement("figcaption"); figCap.innerText = json[x].album; var image = document.createElement("img"); image.id = "picture"; image.width = 100; image.height = 100; image.src = json[x].cover; var display = document.getElementById("results"); fig.appendChild(image); fig.appendChild(figCap); display.appendChild(fig); },它会给出12位数的随机化数据。

答案 1 :(得分:3)

计算行(wc -l)并以随机顺序生成与行号对应的数字列表 - 可能是通过生成临时文件中的数字列表(使用/tmp/,其中包含RAM通常,因此相对较快)。然后按照随机数字的顺序将对应于每个数字的行复制到目标文件。

这会节省时间,因为在文件中寻找新行的数量很多,但它几乎适用于任何大小的文件。

答案 2 :(得分:1)

看看https://github.com/alexandres/terashuf。来自页面:

  

terashuf实现了一种用于改组的准混洗算法   使用有限内存的多TB文本文件

答案 3 :(得分:0)

怎么样: perl <large-input-file -lne 'print rand(), "\t", $_' | sort | perl -lpe 's/^.*?\t//' >shuffled-output-file

答案 4 :(得分:0)

如果文件在内存中可以容纳几个数量级,那么一个选项就是在(比方说)1000个临时文件中随机分配行,然后将每个文件洗牌并连接结果:

perl -we ' my $NUM_FILES = 1000;
           my @fhs;
           for (my $i = 0; $i < $NUM_FILES; ++$i) {
             open $fh[$i], "> tmp.$i.txt"
               or die "Error opening tmp.$i.txt: $!";
           }
           while (<>) {
             $fh[int rand $NUM_FILES]->print($_);
           }
           foreach my $fh (@fhs) {
             close $fh;
           }
         ' < input.txt \
&& \
for tmp_file in tmp.*.txt ; do
  shuf ./"$tmp_file" && rm ./"$tmp_file"
done > output.txt

(当然,临时文件的大小会有一些变化 - 它们都不会完全原始文件大小的千分之一 - 所以如果你使用这种方法,你需要通过错误的更小的文件来给自己一些缓冲。)

答案 5 :(得分:0)

首先,我想说,这不是严格的全局改组解决方案。

通常,我的想法是将大文件分成较小的文件,然后进行随机播放。

  1. 文件拆分成多个片段:

split -bytes=500M large_file small_file_

这会将您的large_file拆分为small_file_aa,small_file_ab ....

  1. 随机播放

shuf small_file_aa > small_file_aa.shuf

您可以尝试多次混合文件以获得近似于全局随机播放的结果。