web-dev-qa-db-ja.com

パラレルを使用して一意の入力ファイルを一意の出力ファイルに処理する

シェルスクリプトの問題があり、入力ファイルでいっぱいのディレクトリ(各ファイルには多くの入力行が含まれています)が与えられ、それらを個別に処理して、各出力を一意のファイルにリダイレクトする必要があります(別名file_1.input needs file_1.outputなどにキャプチャされます)。

Pre-parallel、ある種のタイマーを実行しながら、ディレクトリ内の各ファイルを繰り返し処理してコマンドを実行しますプロセッサーを圧倒しない/ countingテクニック(各プロセスの実行時間が一定であると想定)。ただし、常にそうであるとは限らないので、カスタムコードを記述せずにシェルスクリプトをマルチスレッド化するには、「並列」のようなソリューションを使用するのが最善の方法のようです。

これらの各ファイルを並列処理していくつかの方法を考えました(そしてコアを効率的に管理できるようにしています)が、すべてハックに思えます。私はかなり簡単なユースケースだと思うので、できるだけクリーンに保つことを望みます(そして、並列の例では何も私の問題ではないようです。

何か助けていただければ幸いです!

入力ディレクトリの例:

> ls -l input_files/
total 13355
location1.txt
location2.txt
location3.txt
location4.txt
location5.txt

脚本:

> cat proces_script.sh
#!/bin/sh

customScript -c 33 -I -file [inputFile] -a -v 55 > [outputFile]

更新:以下のOleの回答を読んだ後、私は自分の並列実装の欠けている部分をまとめることができました。彼の答えは素晴らしいですが、ここに私の追加の調査と私が取ったメモがあります:

私は完全なプロセスを実行する代わりに、私の環境で彼のソリューションを証明するための概念実証コマンドから始めることにしました。私の2つの異なる実装(およびメモ)を参照してください。

find /home/me/input_files -type f -name *.txt | parallel cat /home/me/input_files/{} '>' /home/me/output_files/{.}.out

Find(lsではなく、問題を引き起こす可能性があります)を使用して、入力ファイルディレクトリ内の該当するすべてのファイルを検索し、その内容を別のディレクトリとファイルにリダイレクトします。上からの私の問題は、読み取りとリダイレクト(実際のスクリプトは単純でした)でした。そのため、スクリプトをcatに置き換えることは、概念の立証に役立ちました。

parallel cat '>' /home/me/output_files/{.}.out :::  /home/me/input_files/*

この2番目のソリューションは、Parallelの入力変数パラダイムを使用してファイルを読み取りますが、初心者にとっては、これははるかに混乱しました。私にとって、find aとpipeを使用することで、私のニーズは問題​​なく満たされました。

18
J Jones

GNU Parallelは、この種のタスク用に設計されています。

parallel customScript -c 33 -I -file {} -a -v 55 '>' {.}.output ::: *.input

または:

ls | parallel customScript -c 33 -I -file {} -a -v 55 '>' {.}.output

CPUコアごとに1つのジョブを実行します。

GNU Parallelは次の方法で簡単にインストールできます。

wget https://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem

GNU Parallelの紹介ビデオをご覧ください:詳細は https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

27
Ole Tange

これを行う標準的な方法は、キューをセットアップし、キューから何かをプルして処理する方法を知っている任意の数のワーカーをスポーンすることです。これらのプロセス間の通信には、fifo(名前付きパイプ)を使用できます。

以下は、概念を実証するための単純な例です。

簡単なキュースクリプト:

#!/bin/sh
mkfifo /tmp/location-queue
for i in inputfiles/*; do
  echo $i > /tmp/location-queue
done
rm /tmp/location-queue

そして労働者:

#!/bin/sh
while read file < /tmp/location-queue; do
  process_file "$file"
done

process_fileはワーカーのどこかに定義でき、必要なことは何でも実行できます。

これらの2つの要素を用意したら、キュープロセスと任意の数のワーカープロセスを起動する単純なモニターを使用できます。

監視スクリプト:

#!/bin/sh
queue.sh &
num_workers="$1"
i=0
while [ $i < $num_workers ]; do
  worker.sh &
  echo $! >> /tmp/worker.pids
  i=$((i+1))
done
monitor_workers

そこにあります。実際にこれを行う場合は、モニターでfifoをセットアップし、キューとワーカーの両方にパスを渡して、それらが結合されず、fifoの特定の場所に動かないようにすることをお勧めします。私は具体的に答えでこのように設定したので、あなたがそれを読んでいるときにあなたが何を使っているかは明らかです。

5
Shawn J. Goff

もう一つの例:

ls *.txt | parallel 'sort {} > {.}.sorted.txt'

他の例は不必要に複雑であることがわかりましたが、ほとんどの場合、上記があなたが探していたものです。

5

並列化を行うことができる一般的に利用可能なツールはmakeです。 GNU makeと他のいくつかは-jオプションは、並列ビルドを実行します。

.SUFFIXES: .input .output
.input.output:
        process_one_file <$< >[email protected]
        mv -f [email protected] $@

次のようにmakeを実行します(ファイル名に特殊文字が含まれていないと思いますが、makeはこれらの文字には適していません)。

make -j 4 $(for x in *.input; do echo ${x%.*}.output; done)

これは、現在のディレクトリにある大量のファイルに対して同じコマンドを実行するためのものです。

#!/bin/sh
trap 'worker=`expr $worker - 1`' USR1  # free up a worker
worker=0  # current worker
num_workers=10  # maximum number of workers
for file in *.txt; do
    if [ $worker -lt $num_workers ]; then
        {   customScript -c 33 -I -file $file -a -v 55 > `basename $file .txt`.outtxt 
            kill -USR1 $$ 2>/dev/null  # signal parent that we're free
        } &
        echo $worker/$num_worker $! $file  # feedback to caller
        worker=`expr $worker + 1`
    else
        wait # for a worker to finish
    fi
done

これは、各customScriptファイルに対してtxtを実行し、出力をouttxtファイルに入れます。必要に応じて変更してください。これを機能させるための鍵は、SIGUSR1を使用したシグナル処理です。これにより、子プロセスは親プロセスに完了したことを通知できます。スクリプト内のほとんどのステートメントはシェルスクリプトにSIGCHLDシグナルを生成するため、SIGCHLDを使用しても機能しません。コマンドをsleep 1に置き換えてこれを試しました。プログラムは、0.28秒のユーザーCPUと0.14秒のシステムCPUを使用しました。これは約400のファイルにのみありました。

3
Arcege

または単にxargs -P、追加のソフトウェアをインストールする必要はありません:

find . -type f -print0 | xargs -0 -I'XXX' -P4 -n1 custom_script -input "XXX" -output "XXX.out"

オプションの説明:

  • -I'XXX'コマンドテンプレートでファイル名に置き換えられる文字列を設定します
  • -P4は4つのプロセスを並行して実行します
  • -n1は、2つのXXXが見つかった場合でも、実行ごとに1つのファイルのみを配置します
  • -print0および-0連携して、ファイル名に特殊文字(空白など)を含めることができます
0
Piotr Czapla