2012-04-07 14 views
2

我需要多個文件中每行的第二個字段。 '減'不 工作,因爲一些行有前導空格。

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

確實工作,但速度很慢。

有沒有更快的方法來做到這一點?

回答

3
awk '{print $2}' files ... > result 

可能會更快。

2

在使用剪切腳本之前,您是否可以不使用sed刪除前導空格?

例如: sed -e 's/^[ \t]*//'將產生一個文件流沒有前導空格。只需將其輸入到腳本中即可。

+0

尤其是在快速多核計算機上運行腳本的情況下,您會發現將處理劃分爲流程管道的好處。只要確保你實際上將任務分開,而不是重複工作。例如,如果Perl已經花費時間去除前導空格,那麼運行sed也只會佔用更多的CPU時間。即使在離散核心上運行sed和Perl,管道開銷可能也很明顯。 – ctt 2012-04-07 17:32:05

4

我猜測autosplit模式很慢,因爲通過分割線獲得的整個數組必須存儲在內存中。如果你的文件排隊很長,這尤其相關。這個怎麼樣:

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

在這裏我們不處理超過第二字線的一部分。當您使用indexsubstr代替正則表達式時,您還可以測試性能。

+0

['unpack'](http://p3rl.org/unpack)。 – daxim 2012-04-08 15:00:10

0

的Perl:

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

非的Perl:

awk '{print $2}' manyfiles >result 
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%的行有一些主要的空白。詳情請參閱Can Parallel::ForkManager speed up a seemingly IO bound task?

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

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

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

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

因此,在我看來,利用所有核心都有一定的收益。

我沒有嘗試任何其他建議,看看使用Perl + Parallel::ForkManager比其中任何一個更好。

這種方法的一個明顯的缺點是它會交錯源文件中的行。這在你的特定情況下可能並不重要。

+0

我想知道這個問題有多大是IO界限(這意味着分叉可能沒有那麼大的幫助)。 – 2012-04-11 11:22:26

+0

@briandfoy我在老化的雙核筆記本電腦上做了一個測試。在Windows中,腳本的修正版本在兩個進程中大約60%的時間運行,而單個運行10個文件時各有1,000,000行(每個文件中大約20%的行都有前導空格)。我將啓動到Linux並在那裏嘗試。不同之處似乎在於任務管理器的CPU利用率,顯示兩個進程都使用100%的內核。在Linux上嘗試此操作後,我會發佈一個更正的腳本和結果。 – 2012-04-11 13:00:02

+0

@briandfoy似乎確實有利於利用所有可用的內核。看到我更新的答案。 – 2012-04-11 14:46:06

0

如果下面是不夠好,

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

我想嘗試

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

如果這不是一次性的事情,速度真的很重要,你可以寫一個在C中用小修剪工具取代上面的perl

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」只是舉例面膜,會更加更快,如果你不會使用它)

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 
相關問題