Mojoliciousで作ったウェブアプリからバッチを叩きたいという案件があって外部コマンド実行について試行錯誤したのでまとめてみた。
Perlではsystem()
、exec()
、``
なんかで外部コマンド実行できるが、それぞれで挙動が異なる。
そこまで時間のかからない戻り値も見ない処理なら特に何も考えずに``
を使うところだが、処理に時間がかかりウェブアプリ側がそれを待つとタイムアウトしてしまうような要件だったので、特にバックグラウンド実行まわりについて調べてみた。
1. ``
による実行
戻り値: 実行結果
実行結果がそのまま返ってくる。
$ perl -e '$r=`echo test`;print"ret:$r\n"' ret:test
単純にタイプ数が少ないこともあって雑にスクリプト書く時はこれを使う機会が多いのだが・・・
バックグラウンド実行
&つけても何かダメっぽい。戻り値の出力を待ってしまうのだろう。
$ time perl -e '`sleep 5s&`;print"done\n"' done real 0m5.099s user 0m0.051s sys 0m0.040s
2.system()
による実行
戻り値: 実行ステータス
実行ステータスが返ってくるらしい。
成功なら0、失敗ならそれ以外。
$ perl -e '$r=system"echo test";print"ret:$r\n"' test ret:0
$ perl -e '$r=system"ero test";print"ret:$r\n"' ret:-1
バックグラウンド実行
普通に&を後ろにつけることで実現できる。
sleepを待たずにperl自体は終了していることが確認できる。
$ time perl -e 'system"sleep 5s&";print"done\n"' done real 0m0.097s user 0m0.039s sys 0m0.051s
下の例だと(出力順序は実行時状況に依存しちゃうんで必ずではないけど)コードの記述順序とは逆に出力されていることから、一応バックグラウンドで動いていることが見て取れると思う。
$ perl -e 'system"echo echo&";print"print\n"' print echo
3.exec()
による実行
戻り値、というか・・・
スクリプト自身のプロセスが指定したコマンドに変化する。
$ perl -e '$r=exec"echo test";print"ret:$r\n"' test
この例でいうとそのままecho test
に変化してしまうので、その後の$retへの挿入やprintが発生しない。
実用上この関数だけで使う機会はあまり無さそう。
fork()
を利用して
ちょっと面倒ではあるが、fork関数を利用することにより今回の要件も実現できる。
fork()はプロセスを分岐させ、戻り値として子プロセスIDを返す。(呼ばれた側の子なら0が返る)
したがって戻り値で判定し、子プロセス側をexec()で変身させる等で任意のコマンドのバックグラウンド実行ができる。
$ perl -e '$pid=fork();exec"echo child"unless$pid;print"parent\n"' parent child #親プロセス終了後に出力されている
結論
ひとまず今回の「単にバッチを叩きたい」だけならsystem()関数に&を付けて渡すのが楽そう。
バッチの状況やら戻り値やらを使いたいぜってことならfork()関数を絡めた記述を検討するという感じだろうか。
- 作者: Randal L. Schwartz,brian d foy,Tom Phoenix,近藤嘉雪
- 出版社/メーカー: オライリージャパン
- 発売日: 2012/07/25
- メディア: 大型本
- 購入: 7人 クリック: 22回
- この商品を含むブログ (16件) を見る