我需要多个文件中每一行的第二个字段。'cut' 不起作用,因为有些行有前导空格。
perl -anle 'print $F[1]' *manyfiles* > result
确实有效,但速度很慢。
有没有更快的方法来做到这一点?
我需要多个文件中每一行的第二个字段。'cut' 不起作用,因为有些行有前导空格。
perl -anle 'print $F[1]' *manyfiles* > result
确实有效,但速度很慢。
有没有更快的方法来做到这一点?
我猜测自动拆分模式很慢,因为通过拆分线获得的整个数组必须存储在内存中。如果您的文件有很长的行,这一点尤其重要。这个怎么样:
perl -ne 'print $1, "\n" if m/^\s*\S+\s+(\S+)/'
这里我们不处理超出第二个单词的行部分。您还可以在使用index
和substr
代替正则表达式时测试性能。
awk '{print $2}' files ... > result
可能更快。
在使用剪切脚本之前,您不能用 sed 删除前导空格吗?
例如:
sed -e 's/^[ \t]*//'
将产生一个文件流,不带前导空格。只需将其通过管道传输到您的脚本中即可。
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' 只是掩码的例子,如果你不使用它会更快)
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是否比其中任何一个更好。
这种方法的一个明显缺点是它将交错来自源文件的行。在您的特定情况下,这可能很重要,也可能无关紧要。
如果以下还不够好,
perl -nE'say /^\s*\S+\s+(\S+)/' *
我会尝试
perl -ple's/^\s+//' * | cut
如果这不是一次性的事情并且速度真的很重要,您可以用 C 编写一个小的修剪工具来替换perl
上面的内容。
我想为这些解决方案添加一些数字。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
珀尔:
perl -ne 'print "$1\n" if m/\s*\S+\s+(\S+)/' manyfiles >result
非 Perl:
awk '{print $2}' manyfiles >result