2012-03-22 10 views
2

私はこのようなコマンドを実行しようとしています:bashスクリプトをパラレル化して競合条件がなくても `find`と一緒に使うには?

find ./ -name "*.gz" -print -exec ./extract.sh {} \; 

自体が小さいgzファイルを。現在、私のextract.shには以下が含まれています。

明らか
# Start delimiter 
echo "#####" $1 >> Info 
zcat $1 > temp 
# Series of greps to extract some useful information 
grep -o -P "..." temp >> Info 
grep -o -P "..." temp >> Info 
rm temp 
echo "####" >> Info 

私は複数のextract.shのインスタンスを実行する場合、それらはすべて同じファイルに書き込むので、これは並列化ではありません。これを行うスマートな方法は何ですか?

私は80K gzファイルを32コアの大規模な馬力を持つマシンに持っています。

+1

抽出するファイルごとに個別の一時ファイルを使用できない理由はありますか? –

+2

uuoc: 'cat temp | grep'は冗長で、grepは入力ファイルを引数として受け取ります。 –

+0

私は最終的に単一の集約ファイルを望んでおり、これを行う最も効率的な方法を探しています。私の場合、これは追加の80Kファイルを作成することを意味します。あなたはそれが問題ではないと思いますか? – Legend

答えて

0

私は一時ディレクトリを作成します。次に、grepごとに出力ファイルを作成します(処理したファイルの名前に基づいて)。 /tmpの下に作成されたファイルはRAMディスクに置かれているので、たくさんの書き込みでハードドライブをスラッシュしません。

最後にすべてをまとめておいてもいいし、終了時に各grepに別のプロセスを知らせるようにしても、プロセスはすぐにファイルをキャッチして終了することができます。

例:

filename=$(basename $1) 
output="$filename.output" 
extracted="$filename.extracted" 
zcat "$1" > "$extracted" 

echo "#####" $filename > "$output" 
# Series of greps to extract some useful information 
grep -o -P "..." "$extracted" >> "$output" 
grep -o -P "..." "$extracted" >> "$output" 
rm "$extracted" 
echo "####" >> "$output" 
+0

+1の 'tmp'。ありがとうございます。これを見て – Legend

+0

@Legend私はあなたに小さな更新を与えました。 – Dunes

+0

私はなぜこの答えがdownvotedされたのだろうか。また、これには若干の問題があります。 'output =" $ 1.output "とすると、tmpディレクトリにtmpファイルが作成されず、元のディレクトリに作成されます。 – Legend

1

は、すべてのファイル(ただ簡潔かつ明瞭のために)想定

working_dir="`pwd`" 
temp_dir="`mktemp -d`" 
cd "$temp_dir" 
find "$working_dir" -name "*.gz" | xargs -P 32 -n 1 extract.sh 
cat *.output > "$working_dir/Info" 
rm -rf "$temp_dir" 

extract.shは-Zで始まります。

したがって、上記のような検索シーケンスを各レターで起動するときに、26コアを並行して使用することができます。各

find ./ -name "a*.gz" -print -exec ./extract.sh a {} \; & 
find ./ -name "b*.gz" -print -exec ./extract.sh b {} \; & 
.. 
find ./ -name "z*.gz" -print -exec ./extract.sh z {} \; 

独自の集計ファイルを生成する必要がある「検索」(抽出物は、「情報」先のファイルを分離するために、最初のパラメータに取る必要)

したい場合には、大きな集合ファイルだけですべての集計に参加します。

しかし、私はそのアプローチでパフォーマンスを得ることに納得していません。最後に、すべてのファイルコンテンツがシリアル化されます。

ハードディスクのヘッドの動きは、unzip(cpu)の性能ではなく、おそらく制限になります。

しかしさんは

+0

+1。しかし、私は何とか、私が欠けている1ライナーの解決策があると感じています。 – Legend

1

のfindutilsのソースを介して簡単にチェックしてみましょうは、findは、各幹部の子プロセスを開始することを明らかにしました。私はソースが誤っているかもしれませんが、それが続いていくと思います。このため、OSはあなたのコアをまたがって共有するので、あなたはすでに並行しています。そして、仮想メモリの魔法によって、同じ実行可能ファイルは、ほとんどはが同じメモリ空間を共有します。

あなたが実行しようとしている問題は、ファイルのロック/データの混合です。個々の子供が走るごとに、あなたのinfoファイルに情報が送られます。これらは個々のスクリプトコマンドなので、スパゲッティのように出力を組み合わせます。これは、ファイルが順番になることを保証するものではありません!ちょうど個々のファイルの内容のすべてが一緒にとどまるでしょう。

この問題を解決するには、(tempfileを使用して)一時ファイルを作成するシェルの機能を利用し、各スクリプトを一時ファイルにダンプしてから、各スクリプトcatに一時ファイルを保存するだけですinfoファイル。使用後に一時ファイルを削除することを忘れないでください。

一時ファイルがRAMにある場合(tmpfsを参照)、最終ファイルへの書き込みと検索検索の実行を除いて、IO結合が回避されます。

TmpfsはRAMを「ディスクスペース」として使用する特別なファイルシステムです。あなたが許可しているRAMの量に達するでしょう。その量から必要以上のものを使用せず、必要に応じていっぱいになるとディスクにスワップします。ルートとして

  1. をマウントポイントを作成します(私はは/ mnt/RAMディスクまたは/メディア/ RAMディスクを好き)
  2. /etc/fstabを編集
  3. tmpfs /mnt/ramdrive tmpfs size=1G 0 0
  4. 実行アンマウントを追加します。使用するには

    あなたの新しいramdriveをマウントするためのrootとして。それはまた、ブート時にマウントされます。

利用可能なすべてのオプションについては、wikipedia entry on fstabを参照してください。

+0

+1ありがとうございます。あなたは3番目のパラグラフを詳しく教えてください。私は現在tmpfsを探しています。 – Legend

+0

@必ずご確認ください。 tmpfsを使った小さなサンプルプロジェクトについては、[ffox-daemon](https://github.com/srathbun/firefox-tmpfs-daemon)の私のポートをdebianに見てください。私は詳細で私の答えを編集します。 –

1

xargsを使用すると、検索を並行して実行できます。

# Start delimiter 
tmp=`mktemp -t Info.XXXXXX` 
src=$1 
echo "#####" $1 >> $tmp 
zcat $1 > $tmp.unzip 
src=$tmp.unzip 

# Series of greps to extract some useful information 
grep -o -P "..." $src >> $tmp 
grep -o -P "..." $src >> $tmp 
rm $src 
echo "####" >> $tmp 
./extract.sh後で組み合わせることができるすべては、一時ファイルに各 .gzからデータを書き込むために mktempを使用することができるで

find ./ -name "*.gz" -print | xargs --max-args 1 --max-procs 32 ./extract.sh 

--max-procsは、実行される処理の数(デフォルトは1である)を制限します

大きな力がある場合は、最初に解凍することなくzgrepを直接使用することができます。しかし、grep秒後に最初にzcatの方が早い場合があります。とにかく

、後に単一のファイルにすべてのものを組み合わせて:あなたは.gzファイルの順序を気にしている場合./extract.shに2つ目の引数を適用

cat /tmp/Info.* > Info 
rm /tmp/Info.* 

find files/ -name "*.gz" | nl -n rz | sed -e 's/\t/\n/' | xargs --max-args 2 ... 

そして./extract.sh中:

tmp=`mktemp -t Info.$1.XXXXXX` 
src=$2 
0

複数のおそらくextract.shの呼び出しが主なボトルネックになります。明白な最適化は、各ファイルを一度だけ読み込んだ後、必要な順序で要約を出力することです。追加の利点として、レポートが単一のブロックとして書き込まれることが考えられますが、インタリーブされた出力を完全に防ぐことはできません。それでも、私の試みです。 awkの代わりに、Perlでこれをやって

#!/bin/sh 

for f; do 
    zcat "$f" | 
    perl -ne ' 
     /(pattern1)/ && push @pat1, $1; 
     /(pattern2)/ && push @pat2, $1; 
     # ... 
     END { print "##### '"$1"'\n"; 
      print join ("\n", @pat1), "\n"; 
      print join ("\n", @pat2), "\n"; 
      # ... 
      print "#### '"$f"'\n"; }' 
done 

は少しより効率的かもしれませんが、あなたはgrep -Pを使用しているので、私はそれは同じ正規表現構文を保つことができるように便利なの姿。

このスクリプトでは、.gzファイルを複数入力できるため、find -exec extract.sh {} \+またはxargsを使用していくつかの並列処理を開始できます。 xargsを使用すると、各新規プロセス、たとえば100〜500ファイルを1つのバッチで実行することで、順次ジョブと並列ジョブのバランスを見つけることができます。新しいプロセスの数は節約されますが、並列化は失われます。いくつかの実験では、バランスがどのようなものであるべきかを明らかにする必要がありますが、これは私が帽子から数字を抜き取って、それがすでに十分であるかどうかを確認するポイントです。

入力ファイルが十分小さい場合、複数のgrep呼び出しでディスクキャッシュが使い果たされ、Perlを起動するオーバーヘッドよりも速くなることが分かります。

関連する問題