そんな今日この頃でして、、、

コード書いたり映画みたり。努力は苦手だから「楽しいこと」を探していきたい。

Perlの外部コマンド実行についての諸々

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()関数を絡めた記述を検討するという感じだろうか。

初めてのPerl 第6版

初めてのPerl 第6版