並べ替えたいgzip圧縮ファイルが1000個あります。
これを順番に実行すると、手順は非常に簡単に見えます。
find . -name *.gz -exec zcat {} | sort > {}.txt \;
上記のコードが機能するかどうかはわかりませんが(どこかで間違いを犯した場合は訂正してください)、その考えを理解していただければ幸いです。
とにかく、全体をより速くするために、ungzip/sortジョブを並列化したいと思います。また、1000個のプロセスすべてが同時に実行されているのを見たくありません。構成可能な容量を持ついくつかの制限されたジョブキュー(JavaのBlockingQueueまたは.NETのBlockingCollectionなど)があると便利です。この場合、たとえば、10個のプロセスのみが並行して実行されます。
これをシェルで行うことは可能ですか?
グーグルへの短い旅行はこの興味深いアプローチを明らかにします: http://pebblesinthesand.wordpress.com/2008/05/22/a-srcipt-for-running-processes-in-parallel-in-bash/ ==
for ARG in $*; do
command $ARG &
NPROC=$(($NPROC+1))
if [ "$NPROC" -ge 4 ]; then
wait
NPROC=0
fi
done
GNU Parallel:
find . -name *.gz | parallel --files 'zcat {} | sort' | parallel -X -j1 sort -m {} ';' rm {} > sorted
GNU Parallelは、次の方法で簡単にインストールできます。
wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
詳細については、紹介ビデオをご覧ください: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1 そしてチュートリアル(man parallel_tutorial)をウォークスルーします。あなたはそれを愛してコマンドラインを使います。
このタスクにはmake(1)
を選択します。これはシェルではありませんが、make(1)
ジョブサーバーはほぼ正確に必要なものであり、このタスクはmake(1)
の能力。 _gzip -cd
_で始まる行は、タブ文字でインデントされていることに注意してください。これは非常に重要です。 (make(1)
も時々少し古く感じることがあります。)
_$ cat Makefile
TXT := $(wildcard *.gz)
all: $(TXT:.gz=.txt)
%.txt:%.gz
gzip -cd $< | sort > $@
$ cp /usr/share/man/man2/*.gz .
$ ls -l
total 1992
-rw-r--r-- 1 sarnold sarnold 4447 2011-12-06 00:22 aa_change_hat.2.gz
-rw-r--r-- 1 sarnold sarnold 3977 2011-12-06 00:22 aa_change_profile.2.gz
-rw-r--r-- 1 sarnold sarnold 5082 2011-12-06 00:22 accept.2.gz
...
$ time make -j 10
gzip -cd aa_change_hat.2.gz | sort > aa_change_hat.2.txt
gzip -cd aa_change_profile.2.gz | sort > aa_change_profile.2.txt
gzip -cd accept.2.gz | sort > accept.2.txt
gzip -cd accept4.2.gz | sort > accept4.2.txt
gzip -cd access.2.gz | sort > access.2.txt
...
gzip -cd write.2.gz | sort > write.2.txt
gzip -cd writev.2.gz | sort > writev.2.txt
real 0m0.259s
user 0m0.190s
sys 0m0.020s
$ rm w*txt
$ make
gzip -cd wait.2.gz | sort > wait.2.txt
gzip -cd wait3.2.gz | sort > wait3.2.txt
gzip -cd wait4.2.gz | sort > wait4.2.txt
gzip -cd waitid.2.gz | sort > waitid.2.txt
gzip -cd waitpid.2.gz | sort > waitpid.2.txt
gzip -cd write.2.gz | sort > write.2.txt
gzip -cd writev.2.gz | sort > writev.2.txt
$
_
_rm w*txt
_コマンドを使用すると、make(1)
は何かを実行するために必要な最小限の作業のみをインテリジェントに実行することに注意してください。
多くの圧縮ファイルの非圧縮コンテンツをソートし、結果を非圧縮ファイルに保存します。
find . -type f -name '*.gz'
-exec sh -c 'for n; do zcat "$n" | sort -o "$n.txt"; done' sh {} +
これにより、for
ループが実行されます
for n; do
zcat "$n" | sort -o "$n.txt"
done
一度にできるだけ多くのファイルを使用します。 in X
がないfor
ループは、デフォルトで"$@"
を反復処理します。
sh -c
シェルはfind
によって呼び出され、ファイルパスはできるだけ多くなります(最後に+
ではなく\;
があるため)。これらのパスは次のようになります。 sh -c
の$@
シェルで使用できます。
元のコマンドでは、
find . -name *.gz -exec zcat {} | sort > {}.txt \;
あなたはいくつかの問題を抱えています:
*.gz
は引用符で囲まれていません。これは、シェルが現在のディレクトリ内のファイル名を使用してファイル名のグロブを実行することを意味します。
-exec
は、パイプラインではなく、単純なコマンドのみを実行できます。
通常のファイルに制限することはありません。つまり、理論的には、名前がsomething.gz
の-ディレクトリを取得できるということです。
GNU xargs
を使用すると、次のことができます。
xargs -P4 -n 10 -r0a <(find . -name '*.gz' -type f -print0) sh -c '
for file do
zcat < "$file" | sort > "$file.txt"
done' sh {} +
これにより、最大4つのsh
が並行して呼び出され、それぞれが最大10個のファイルをループで次々に処理します。