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

# 概要

一つ一つで見るとCPU負荷がそれほどじゃないコマンドを一気にバルク実行（並列実行）したいですよね？
大量のコマンドをリミット付けてバルク実行させますよね？
並列実行コントローラシェルスクリプトです。
投稿するには規模が大きいかな？とか思いましたがネットにサンプルないので必要かなと…
Cygwinでも動きます（たぶん）

# コントローラソース

ディレクトリ配下のファイルをfindし実行用スクリプトに順次渡してバルク実行しています。
このソースは関数化してないので構造の解析把握が必要になります。
バルク実行は取り扱いがあれなので、内部動作は十分知っておいた方が良いと思います。

```bash: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
```

# 呼ばれる側（参考）

```bash: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
```

# 実行結果

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

```shell-session
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はバルク数（同時実行数）です。実行中に変更も効きますけどタイミング悪いと飛びます。
こんな感じに使います

```shell-session
% echo 3 > bulk_limit_bulk_kicker.sh.cfg
```

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

```shell-session
% rm exists_loop_bulk_kicker.sh.flg
```