入力ファイルでいっぱいのディレクトリ(各ファイルには多くの入力行が含まれています)があるシェルスクリプトの問題があります。これを個別に処理して、各出力を一意のファイルにリダイレクトする必要があります(別名file_1.inputをfile_1からキャプチャする必要があります)。 .outputなど)。
準備、プロセッサを圧倒しないように、ある種のタイマー/計算技術を実行しながら、ディレクトリ内の各ファイルを繰り返して命令を実行します(各プロセスに一定のランタイムがあると仮定)。しかし、これは必ずしもそうではないことがわかっているので、「並列」などのソリューションを使用することは、カスタムコードを書かずにシェルスクリプトのマルチスレッドを得るための最良の方法のようです。
各ファイルを並列に処理し、コアを効率的に管理する方法をいくつか考えてみましたが、すべて混乱しているようです。私は非常に簡単だと思うユースケースを持っているので、できるだけきれいに保ちたいです(並列例のどれも私にとっては問題にならないようです)。
どんな助けでも大変感謝します!
入力ディレクトリの例:
> 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番目の解決策は、パラレル入力変数パラダイムを使用してファイルを読み込みますが、初心者にとっては混乱します。私にとっては、find aとパイプラインを使用することは私のニーズによく合いました。
答え1
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
答え2
これを行う標準的な方法は、キューを設定し、キューからコンテンツを取得して処理する方法を知っているワーカーを必要なだけ作成することです。 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の特定の場所に固定しないようにすることをお勧めします。私はあなたがそれを読んだときに何をしているのかを明確に知ることができるように、私の答えで特にこのように設定しました。
答え3
他の例:
ls *.txt | parallel 'sort {} > {.}.sorted.txt'
他の例は不必要に複雑であることがわかりましたが、ほとんどの場合、上記の例はあなたが探していたものかもしれません。
答え4
以下は、現在のディレクトリにある大規模ファイルセットに対する同じコマンドです。
#!/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個のファイルしか含まれていません。