Edited at

シェルスクリプトでコマンド多重コントロール

More than 5 years have passed since last update.


概要

一つ一つで見るとCPU負荷がそれほどじゃないコマンドを一気にバルク実行(並列実行)したいですよね?

大量のコマンドをリミット付けてバルク実行させますよね?

並列実行コントローラシェルスクリプトです。

投稿するには規模が大きいかな?とか思いましたがネットにサンプルないので必要かなと…

Cygwinでも動きます(たぶん)


コントローラソース

ディレクトリ配下のファイルをfindし実行用スクリプトに順次渡してバルク実行しています。

このソースは関数化してないので構造の解析把握が必要になります。

バルク実行は取り扱いがあれなので、内部動作は十分知っておいた方が良いと思います。


bulk_kicker.sh

#!/bin/bash

if [ $# -ne 1 ]; then
echo "usage: " 1>&2
echo " ${0##*/} /path/name" 1>&2
exit 1
fi

CheckDir=$1

ReturnNo=0
ExitCheckFileName="exists_loop_${0##*/}.flg"

# 並列同時実行数 初期値
# カレントディレクトリの config に多重数を書き込んで変更する
BulkLimit=6
BulkLimitControlFile="bulk_limit_${0##*/}.cfg"

# 終了判定用ファイル作成、存在しなくなれば終了
# ※ 緊急時にこのファイルを削除するとループ終了
echo 'if not exists this file, next step exit script.' > $ExitCheckFileName

echo "**** start ****"

# ファイルリスト配列作成
# ※ ファイルリストをループする場合
echo "[$CheckDir]"
pushd $CheckDir >& /dev/null
unset ArrayFiles;
for LINE in `find ./ -name "*" | sort`; do
if
[ -s $LINE ]; then
ArrayFiles+=($LINE)
fi
done
unset LINE;
popd >& /dev/null

unset pids
for el in ${ArrayFiles[@]}; do
if
[ ! -e $ExitCheckFileName ]; then
# 終了判定用ファイル、存在しなくなれば終了
break;
fi

# 多重起動制御数取り込み
if [ ! -e $BulkLimitControlFile ]; then
echo $BulkLimit > $BulkLimitControlFile
else
BulkLimit=`head -n 1 $BulkLimitControlFile`
fi

while true
do
# 現多重数取得
pnum=0
unset switchPids
for pid in ${pids[@]}; do
GetStatus=`ps -p $pid | grep $pid`
if [ "$GetStatus" != "" ]; then
switchPids+=($pid)
let pnum++
fi
done
unset pids
pids=(${switchPids[@]})
if [ $pnum -lt $BulkLimit ]; then
break;
fi
sleep 1
done

# 多重起動するshを & 付きで実行
dno=`printf "%04d" ${#pids[*]}`
echo "[`date '+%Y/%m/%d %T'`] "$dno 'sh ./bulker.sh' $CheckDir/$el
eval 'sh ./bulker.sh' $CheckDir/$el &
pids+=($!)
# for pid in ${pids[@]}; do
# echo "pids $pid"
# done
done

# 走っているプロセスの終了を待つ
while true
do
# 現多重数取得
unset switchPids
for pid in ${pids[@]}; do
GetStatus=`ps -p $pid | grep $pid`
# GetId=`ps | grep $pid | awk '{print $1}'`
if [ "$GetStatus" != "" ]; then
switchPids+=($pid)
fi
done
unset pids
pids=(${switchPids[@]})
if [ "${#pids[*]}" == "0" ]; then
break;
fi
sleep 0.5
done

# dno=`printf "%04d" ${#pids[*]}`
# echo "pids $dno"

echo "**** end ****"

exit $ReturnNo



呼ばれる側(参考)


bulker.sh

#!/bin/bash

# バルク実行テスト用なので適当にsleep入れてます
# 引数のファイルサイズをechoするだけのスクリプトです
path=$1
fsize=`wc -c $1 | awk '{print $1}'`
fsize=`printf "%10d" $fsize`
sleep 1
echo "[`date '+%Y/%m/%d %T'`] $fsize $path"
sleep 1


実行結果

相対パスを指定してますが、シェルを使う場合は絶対パスが良いです。

ekaneko@bibian ~/work/bulker % bash bulk_kicker.sh ../

**** start ****
[../]
[2014/05/23 15:34:17] 0000 sh ./bulker.sh ..//./
wc: ..//./: ディレクトリです
[2014/05/23 15:34:17] 0001 sh ./bulker.sh ..//./arg_check.sh
[2014/05/23 15:34:17] 0002 sh ./bulker.sh ..//./bulker
wc: ..//./bulker: ディレクトリです
[2014/05/23 15:34:17] 0003 sh ./bulker.sh ..//./bulker/bulk_kicker.sh
[2014/05/23 15:34:17] 0004 sh ./bulker.sh ..//./bulker/bulk_limit_bulk_kicker.sh.cfg
[2014/05/23 15:34:17] 0005 sh ./bulker.sh ..//./bulker/bulker.sh
[2014/05/23 15:34:18] 0 ..//./
[2014/05/23 15:34:18] 87 ..//./arg_check.sh
[2014/05/23 15:34:18] 0 ..//./bulker
[2014/05/23 15:34:18] 2561 ..//./bulker/bulk_kicker.sh
[2014/05/23 15:34:18] 2 ..//./bulker/bulk_limit_bulk_kicker.sh.cfg
[2014/05/23 15:34:18] 145 ..//./bulker/bulker.sh
[2014/05/23 15:34:20] 0000 sh ./bulker.sh ..//./bulker/exists_loop_bulk_kicker.sh.flg
[2014/05/23 15:34:20] 0001 sh ./bulker.sh ..//./mysql
wc: ..//./mysql: ディレクトリです
[2014/05/23 15:34:20] 0002 sh ./bulker.sh ..//./mysql/mymysql.sh
[2014/05/23 15:34:20] 0003 sh ./bulker.sh ..//./mysql/mymysql_test.sh
[2014/05/23 15:34:21] 48 ..//./bulker/exists_loop_bulk_kicker.sh.flg
[2014/05/23 15:34:21] 0 ..//./mysql
[2014/05/23 15:34:21] 1681 ..//./mysql/mymysql.sh
[2014/05/23 15:34:21] 379 ..//./mysql/mymysql_test.sh
**** end ****
ekaneko@bibian ~/work/bulker %


解説

簡単に…

実行時に2つファイルを生成します(存在しない場合)

1. bulk_limit_bulk_kicker.sh.cfg

2. exists_loop_bulk_kicker.sh.flg


  1. limitはバルク数(同時実行数)です。実行中に変更も効きますけどタイミング悪いと飛びます。
    こんな感じに使います

% echo 3 > bulk_limit_bulk_kicker.sh.cfg


  1. exists_loop は緊急終了トリガーファイルです。消すとループを抜けるので移行の処理を行いません。
    こんな感じに使います。

% rm exists_loop_bulk_kicker.sh.flg