web-dev-qa-db-ja.com

クエリr.eforループスクリプトを並行して実行

シェルスクリプトには次のものがあります。

for file in $local_dir/myfile.log.*; 
    do 
        file_name=$(basename $file); 
        server_name=$(echo $file_name | cut -f 3 -d '.');
        file_location=$(echo $file);

        mv $file_location $local_dir/in_progress1.log

        mysql -hxxx -P3306 -uxxx -pxxx -e "set @server_name='${server_name}'; source ${sql_script};"

        rm $local_dir/in_progress1.log
    done

基本的に、基準に一致するディレクトリ内のすべてのファイルを取得し、ファイル名からサーバー名を抽出してから、処理のためにMySQLスクリプトに渡します。

私が疑問に思っているのは、完了するのにそれぞれ60秒かかる10個のファイルがあり、5分後にシェルスクリプトの2番目のインスタンスを開始することです。

  • a)2番目のスクリプトはまだ処理されていないファイルを表示しますか
  • b)ファイルを削除すると、最初のインスタンスで問題が発生しますか

または、問題なく並行して実行できますか?

3
IGGt

「60秒」(さらには「5分」)は適切な見積もりであり、2番目のバッチが開始されたときに最初のバッチがまだ進行中であるリスクがあると想定されます。バッチを分離したい場合(そして時折オーバーラップするログファイル以外に問題がない場合)、より良いアプローチは、進行中のファイル命名規則の一部としてバッチ番号を作成することです。

このようなもの:

[[ -s ]] $local_dir/batch || echo 0 > $local_dir/batch
batch=$(echo $local_dir/batch)
expr $batch + 1 >$local_dir/batch

forループの前、そしてループの開始時に、パターンが実際のファイルと一致することを確認します

[[ -f "$file" ]] || continue

ファイル名にバッチ番号を使用します。

mv $file_location $local_dir/in_progress$batch.log

そして4番目に。これにより、衝突のリスクが軽減されます。

2
Thomas Dickey

問題に対するいくつかの良い解決策を提供する上記の答えがありますが、私は問題が何であるかについてなぜについて少し説明したいと思いました。

ほとんどの場合:名前を変更したログファイル(進行中のログファイル)が基準を満たしていない限り、おそらくでこれを実行しても安全です最小リスク。ただし、まだいくつかのエラーが発生します...

ファイルのリストは、スクリプトの実行時に生成されます。したがって、最終的に発生するのは次のとおりです。

Script A10 filesのリストを取得します。処理を開始し、(残り5つ)5 filesscript B5 remaining filesのリストを取得し、処理を開始します。 Script aは、リストの次のファイルの処理に進みます(これは、script Bが処理を開始したファイルと同じです)。ファイルの名前が変更されているため、エラーが発生します。したがって、エラー処理を使用すると、理論的にはリスト内の次の項目に移動して、問題なく機能する可能性があります。しかし、明らかに星が揃う可能性は常にありますが、スクリプトが同じファイルに同時にヒットし、予期しないことが起こります。あなたがそうするようにそのリスクを比較検討してください。

潜在的により洗練された解決策は、これをpythonスクリプトに変換してparallel for loopsを調べることです。これにより、単一のforループを作成し、それを並行して実行して、1つのスクリプトで実行できるようになります。 2つ以上の作品。

1
Gravy

これを行う別の方法は、スクリプトに単純なバッチキューを実装することです。

スクリプトの開始時に、次のようなことを行うことができます。

mkdir -p $localdir/batch
BATCHTMP=$(mktemp batch.XXXXXXXXXX)
MYBATCH="$localdir/batch/batch.$$"

# get list of current log files
find $local_dir/ -name 'myfile.log.*' > "$BATCHTMP"

# exclude any log files already in other batches
grep -vF -f <(sort -u $localdir/batch/batch.*) < "$BATCHTMP" > "$MYBATCH"

rm -f "$BATCHTMP"

# only process log files that are in my batch
for lf in $(cat "$MYBATCH") ; do
....
# somewhere in here, mv or rm the logfile being processed
# so it doesn't get processed again in a later batch run
done

rm -f "$MYBATCH"

もちろん、これは何をする必要があるかについての簡単な概要にすぎません。

ところで、これは、バッチファイルを生成してからメインスクリプトを実行するだけのラッパースクリプトで実行することもできます。

1
cas