如何拆分文件并将它们并行处理然后再缝合? UNIX

时间:2015-03-13 13:06:13

标签: bash perl unix split cat

我有一个文本文件infile.txt

abc what's the foo bar.
foobar hello world, hhaha cluster spatio something something.
xyz trying to do this in parallel
kmeans you're mean, who's mean?

文件中的每一行都将通过此perl命令处理到out.txt

`cat infile.txt | perl dosomething > out.txt`

想象一下,如果文本文件是100,000,000行。我想并行化bash命令,所以我尝试了这样的事情:

$ mkdir splitfiles
$ mkdir splitfiles_processed
$ cd splitfiles
$ split -n3 ../infile.txt
$ for i in $(ls); do "cat $i | perl dosomething > ../splitfiles_processed/$i &"; done
$ wait
$ cd ../splitfiles_processed
$ cat * > ../infile_processed.txt

但是,有一种不那么冗长的方法吗?

3 个答案:

答案 0 :(得分:15)

@Ulfalizer的答案为您提供了解决方案的良好提示,但缺乏细节。

您可以使用GNU parallel(Debian上的apt-get install parallel

因此,您可以使用以下命令解决问题:

parallel -a infile.txt -l 1000 -j10 -k --spreadstdin perl dosomething > result.txt

这是论证的意义

-a: read input from file instead of stdin
-l 1000: send 1000 lines blocks to command
-j 10: launch 10 jobs in parallel
-k: keep sequence of output
--spreadstdin: sends the above 1000 line block to the stdin of the command

答案 1 :(得分:6)

我自己从未尝试过,但GNU parallel可能值得一试。

此处摘自手册页(parallel(1)),与您当前正在进行的操作类似。它也可以用其他方式分割输入。

EXAMPLE: Processing a big file using more cores
       To process a big file or some output you can use --pipe to split up
       the data into blocks and pipe the blocks into the processing program.

       If the program is gzip -9 you can do:

       cat bigfile | parallel --pipe --recend '' -k gzip -9 >bigfile.gz

       This will split bigfile into blocks of 1 MB and pass that to gzip -9
       in parallel. One gzip will be run per CPU core. The output of gzip -9
       will be kept in order and saved to bigfile.gz

这是否值得,取决于您的处理CPU密集程度。对于简单的脚本,您将花费大部分时间将数据移入和移出磁盘,并行化并不会让您获得更多。

您可以通过GNU Parallel作者here找到一些介绍性视频。

答案 2 :(得分:4)

假设您的限制因素不是您的磁盘,您可以使用fork()并特别Parallel::ForkManager在perl中执行此操作:

#!/usr/bin/perl

use strict;
use warnings;

use Parallel::ForkManager;

my $max_forks = 8; #2x procs is usually optimal

sub process_line {
    #do something with this line
}

my $fork_manager = Parallel::ForkManager -> new ( $max_forks ); 

open ( my $input, '<', 'infile.txt' ) or die $!;
while ( my $line = <$input> ) {
    $fork_manager -> start and next;
    process_line ( $line );
    $fork_manager -> finish;
}

close ( $input );
$fork_manager -> wait_all_children();

做这样的事情的缺点是合并你的输出。每个并行任务都不会必然按照它开始的顺序完成,因此您有关于序列化结果的各种潜在问题。

您可以使用flock之类的方法解决这些问题,但是您需要小心,因为过多的锁定操作可能会首先夺走您的并行优势。 (因此我的第一个声明 - 如果你的限制因素是磁盘IO,那么并行性无论如何都无济于事)。

虽然有各种可能的解决方案 - 但是在perl文档中写了一整章:perlipc - 但请记住,您也可以使用Parallel::ForkManager检索数据。