このバッチがきちんと終わったら、あのバッチを動かしたい。なんて事はよくあると思います。今までそのへんの事はcronで制御していたのですが、バッチがクソ増えてきてスケジューリングがつらたん。。という状態になり、こうなったら一連の処理を行うバッチはシェルで制御した方が楽だよね、そうだよね、じゃあ調べてね、ってなった時のことを書きます。
※ ジョブ管理ツールとかは無しで
※ 制御するとかありますが、そんな難しいことではないです
※ ちょっと回りくどい感じで書いていますので出来れば最後まで読んでみてください
やりたいこと
バッチA (hoge.php) 実行
↓
エラー無し → (No) → バッチ終了
↓ (Yes)
バッチB (piyo.php) 実行
やりたいことはこんな感じです。
最初に書いたシェル
#!/bin/bash PHP_PATH=`which php` BATCH_DIR="/your/batch/dir" ${PHP_PATH} ${BATCH_DIR}/hoge.php \ && ${PHP_PATH} ${BATCH_DIR}/piyo.php
※>/dev/null 2>&1
これ書く書かない問題は別とします
普通にシェルで制御するって言われるとこんな感じで、&&
でつなげて実行する方法を思いつくのではないでしょうか。
でもね、これですんなりいけるバッチは、エラーがあった場合にきちんと異常終了(終了コード 0 以外を返却)するように作られているバッチだけなんですよね。
例えば、hoge.phpがこんな風になっていたとします。
<?php try { // なんらかの処理 doSomething(); // ここでエラーが発生したと仮定する } catch(\Exception $e) { // エラーハンドリング exit(); }
これだとバッチ的には処理は失敗した事になるのですが、シェル的にはバッチは正常終了 (リターンコードは 0) していることになってしまい、上記の最初に書いたシェルだと、piyo.phpが実行されてしまいます。
上記のphpが、こんな感じで
<?php try { // なんらかの処理 doSomething(); // ここでエラーが発生したと仮定する } catch(\Exception $e) { // エラーハンドリング exit(1); }
きちんと異常終了(終了コード 0 以外を返す)していれば問題ありません。
それか、
<?php // なんらかの処理 if (doSomething() === false) { throw new \Exception("Error"); }
こんな感じで例外が発生して終了となっている場合です。
(この場合、リターンコードは 255 が返るようです)
新たに作るバッチはこの辺を意識すれば上記で対応できるのですが、古のバッチはいたるところでexit();
で処理を終了していました。。(そのくせ例外はしっかりキャッチしてやがる。。)
そこでちょっと悩んだ末、一旦、以下の様なシェルを書いてみました。
一旦悩んで書いたシェル
config.sh
#! /bin/sh #-------------------------------------------------------------------------------- # config #-------------------------------------------------------------------------------- # 終了コードが 99(正常終了) 以外はexit # 今回は99を正常とみなす is_success() { if [ $1 -ne "99" ]; then exit 1 fi } # php path PHP_PATH=`which php` # batch dir BATCH_DIR="/your/batch/dir"
sample.sh
#! /bin/sh #-------------------------------------------------------------------------------- # sample #-------------------------------------------------------------------------------- # read config DIR_NAME=$(cd $(dirname $0) && pwd) . "${DIR_NAME}/config.sh" # hoge.php ${PHP_PATH} ${BATCH_DIR}/hoge.php # 成功したかチェック is_success $? # piyo.php ${PHP_PATH} ${BATCH_DIR}/piyo.php
設定ファイルconfig.sh
と実行ファイルsample.sh
は同階層にある想定です。
今後似たようなケースがでてくるかもしれないので、設定ファイルと実行ファイルは別に分けてみました。
なんでこう書いてみたのか経緯
明示的に exit(X); と書かないとリターンコードは 0 となる ※ 0 < X < 255, 例外で終了時以外
これは無知すぎたのもあったのですが、明示的にexit(X);
と書かないとリターンコードは正常終了0になります。
※ このX(int)の値(0~254)がシェル側でリターンコードとしてみなされる値です
※ 例外で終了時は255が返却されます
※ Xがstringの場合は別です
※ PHP: exit – Manual
つまりエラーがあった際に、たとえexit();
で終了させていても、その処理は正常に行われたとみなされてしまいます。
先にも書きましたが古のバッチは、いたるところでexit();
で終了させておりました。
なんで 99 を正常終了とするんだぜ?
エラーで終了しているところ(exit();
で終わらせている箇所)をgrepして、exit(1);
と書くのがめんどくさかったからです。
なので必ず成功していたら通る箇所に、exit(99);
と追記しました。
※ 99は単に被らないであろう数字を選んだだけです(1〜254であればなんでもよかった)
でもね、、なんかキモいなぁ
こう書いてみて、まぁこれでもいいのかなぁなんて思ったりもしたんですが、やっぱりなんかキモいですよね。。
制御したいバッチの数が増えるたびに、is_success $?
をはさまないといけない部分では無くて、0を正常終了として扱っていないところです。PHP: exit – Manual を見ても、ステータス 0 は、 プログラムを正常終了させる際に使用します。と書いておりますし。
99を正常終了として扱うなんて、コンテキストを知らない人が見たら気が触れちまうかもしれません。
結局どうしたの
結局、異常終了時はexit(1);
(※終了コード 0 以外) ときちんと書いて、最初に書いた至極シンプルな
#!/bin/bash PHP_PATH=`which php` BATCH_DIR="/your/batch/dir" ${PHP_PATH} ${BATCH_DIR}/hoge.php \ && ${PHP_PATH} ${BATCH_DIR}/piyo.php
でいくことにしました。
まとめ
- バッチは終了コードを意識して書いたほうがいい
- 正常終了時は
exit(0);
と書く ※書かなくてもいいと思うけど - 異常終了時は
exit(1);
(※終了コード 0 以外) と書く - 上記のような作りになっていればシェルも書きやすい