web-dev-qa-db-ja.com

シェルタスクの制限付きキューを作成するにはどうすればよいですか?

並べ替えたいgzip圧縮ファイルが1000個あります。

これを順番に実行すると、手順は非常に簡単に見えます。

find . -name *.gz -exec zcat {} | sort > {}.txt \;

上記のコードが機能するかどうかはわかりませんが(どこかで間違いを犯した場合は訂正してください)、その考えを理解していただければ幸いです。

とにかく、全体をより速くするために、ungzip/sortジョブを並列化したいと思います。また、1000個のプロセスすべてが同時に実行されているのを見たくありません。構成可能な容量を持ついくつかの制限されたジョブキュー(JavaのBlockingQueueまたは.NETのBlockingCollectionなど)があると便利です。この場合、たとえば、10個のプロセスのみが並行して実行されます。

これをシェルで行うことは可能ですか?

5
DNNX

グーグルへの短い旅行はこの興味深いアプローチを明らかにします: 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
1
jstarek

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)をウォークスルーします。あなたはそれを愛してコマンドラインを使います。

4
Ole Tange

このタスクには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)は何かを実行するために必要な最小限の作業のみをインテリジェントに実行することに注意してください。

2
sarnold

多くの圧縮ファイルの非圧縮コンテンツをソートし、結果を非圧縮ファイルに保存します。

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 \;

あなたはいくつかの問題を抱えています:

  1. *.gzは引用符で囲まれていません。これは、シェルが現在のディレクトリ内のファイル名を使用してファイル名のグロブを実行することを意味します。

  2. -execは、パイプラインではなく、単純なコマンドのみを実行できます。

  3. 通常のファイルに制限することはありません。つまり、理論的には、名前がsomething.gzの-​​ディレクトリを取得できるということです。

0
Kusalananda

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個のファイルをループで次々に処理します。

0