2

我需要多个文件中每一行的第二个字段。'cut' 不起作用,因为有些行有前导空格。

perl -anle 'print $F[1]' *manyfiles* > result 

确实有效,但速度很慢。

有没有更快的方法来做到这一点?

4

8 回答 8

4

我猜测自动拆分模式很慢,因为通过拆分线获得的整个数组必须存储在内存中。如果您的文件有很长的行,这一点尤其重要。这个怎么样:

perl -ne 'print $1, "\n" if m/^\s*\S+\s+(\S+)/'

这里我们不处理超出第二个单词的行部分。您还可以在使用indexsubstr代替正则表达式时测试性能。

于 2012-04-07T18:16:33.713 回答
3
awk '{print $2}' files ... > result

可能更快。

于 2012-04-07T17:20:00.263 回答
2

在使用剪切脚本之前,您不能用 sed 删除前导空格吗?

例如: sed -e 's/^[ \t]*//'将产生一个文件流,不带前导空格。只需将其通过管道传输到您的脚本中即可。

于 2012-04-07T17:22:02.913 回答
1
sed -rn 's/\s*[^\s]+\s+([^\s]+).*/\1/p' file1 file2 > parsed_text

应该更快。

或者您可以使用它来构建文件列表:

find /path/to/files/ -type f -iname "*" -print0 | xargs -0 -I {} sed … 

('iname' 只是掩码的例子,如果你不使用它会更快)

于 2012-04-07T22:10:04.757 回答
1

Parallel::ForkManager可能会有所帮助,尤其是当您不需要按源文件对输出进行分组时。但是,增加同时访问磁盘的进程数量也可能会导致速度变慢,但值得一试。

以下示例取自Parallel::ForkManager手册页(已更正前版本中存在的明显错误):

#!/usr/bin/env perl

use strict; use warnings;

use Parallel::ForkManager;

my ($maxproc) = @ARGV;
my @files = ('01' .. '10');

my $pm = Parallel::ForkManager->new($maxproc);

for my $file (@files) {
    my $pid = $pm->start and next;
    my $ret = open my $h, '<', $file;

    unless ($ret) {
        warn "Cannot open '$file': $!";
        $pm->finish;
    }

    while (my $line = <$h>) {
        next unless $line =~ /^\s*\S+\s+(\S+)/;
        print "$1\n";
    }

    $pm->finish;
}

$pm->wait_all_children;

我用 10 个文件运行上面的脚本,每个文件有 1_000_000 行。在每个文件中,20% 的行都有一些前导空格。请参阅Parallel::ForkManager 可以加速看似 IO 绑定的任务吗?详情。

# 同步
# echo 3 > /proc/sys/vm/drop_caches
$ /usr/bin/time -f '%Uuser %Ssystem %Eelapsed %PCPU' ./process.pl 1 > 输出
24.44user 0.93system 0:29.08elapsed 87%CPU

$ rm 输出
# 同步
# echo 3 > /proc/sys/vm/drop_caches
$ /usr/bin/time -f '%Uuser %Ssystem %Eelapsed %PCPU' ./process.pl 2 > 输出
24.95user 0.91system 0:18.31elapsed 141%CPU

$ rm 输出
# 同步
# echo 3 > /proc/sys/vm/drop_caches
$ /usr/bin/time -f '%Uuser %Ssystem %Eelapsed %PCPU' ./process.pl 4 > 输出
24.70user 0.88system 0:17.45elapsed 146%CPU

$ rm 输出
# 同步
# echo 3 > /proc/sys/vm/drop_caches
$ /usr/bin/time -f '%Uuser %Ssystem %Eelapsed %PCPU' ./process.pl 1 > 输出
25.31user 0.95system 0:29.72elapsed 88%CPU

所以在我看来,利用你所有的核心有一些好处。

我没有尝试任何其他建议来查看使用 Perl+ Parallel::ForkManager是否比其中任何一个更好。

这种方法的一个明显缺点是它将交错来自源文件的行。在您的特定情况下,这可能很重要,也可能无关紧要。

于 2012-04-07T18:35:44.220 回答
0

如果以下还不够好,

perl -nE'say /^\s*\S+\s+(\S+)/' *

我会尝试

perl -ple's/^\s+//' * | cut

如果这不是一次性的事情并且速度真的很重要,您可以用 C 编写一个小的修剪工具来替换perl上面的内容。

于 2012-04-07T21:05:41.527 回答
0

我想为这些解决方案添加一些数字。aaqp.txt文件为 130 MB,1,000,000 行,平均有 7 个字段。我实际上为此生成了 50+GB 的样本数据,但我太不耐烦了,无法等待其中任何一个完成。

$ time perl -anle 'print $F[1]' aaqg.txt > result

real    0m18.526s
user    0m18.368s
sys     0m0.089s

$ time awk '{print $2}' aaqg.txt  > result

real    0m4.051s
user    0m3.592s
sys     0m0.091s

$ time perl -nE 'say $1 if m/\s*\S+\s+(\S+)/' aaqg.txt > result

real    0m2.009s
user    0m1.901s
sys     0m0.066s

$ time perl -nE'say /^\s*\S+\s+(\S+)/' aaqg.txt > result

real    0m2.069s
user    0m1.813s
sys     0m0.069s
于 2012-04-11T12:13:10.160 回答
0

珀尔:

perl -ne 'print "$1\n" if m/\s*\S+\s+(\S+)/' manyfiles >result

非 Perl:

awk '{print $2}' manyfiles >result
于 2012-04-07T18:22:08.073 回答