PHPでUNIXコマンドを実行する方法っていろいろあって迷っちゃう♪と困り果てているあなたに朗報です。この記事では、以下の5つの方法を比較して解説します。
- exec()
- passthru()
- system()
- shell_exec()
- 実行演算子 `…`
それぞれの使い方と特徴を解説し、どんな時にどれを使うべきかをズバリ教えちゃいます。
PHPで外部コマンドを実行する方法の比較表
最初に結論を言いますね(親切設計)。コマンドの実行結果が文字列ならばexec()
関数、バイナリならばpassthru()
関数を使いましょう。他の関数は使いません。
方法 | コール時の出力 | 実行結果の取得 | 終了ステータスの取得 | 出力の種類 | 説明 |
---|---|---|---|---|---|
exec() | なし | 可能 | 可能 | テキスト | テキストを扱うならこれ |
passthru() | あり | 不可 | 可能 | バイナリ | バイナリを扱うならこれ |
system() | あり | 最後の行のみ | 可能 | テキスト | C言語のsystem関数に似ている |
shell_exec() | なし | 可能 | 不可 | テキスト | 実行演算子と同じ |
実行演算子 | 可能 | 不可 | テキスト | shell_exec()関数と同じ |
ご覧のとおり、実行結果と終了ステータスの両方を完全に取得できるのは、exec()
関数だけです。そしてバイナリセーフなのはpassthuru()
関数だけです。
system()
関数は、コールするとすぐに実行結果を表示してしまうので、あまり使い勝手が良くないと私は思います。一旦変数に入れたいですよね。
shell_exec()
関数と実行演算子は、まったく同じように動作します。終了ステータス(コマンドの実行が成功したか失敗したかの情報)を取得できないので、あまりおすすめできません。
exec()
exec()関数の書式
exec($command, $output, $result_code)
- 第一引数
$command
は、実行するコマンドを指定します。 - 第二引数
$output
を指定すると、コマンドの実行結果が格納されます。実行結果の各行が配列の要素として格納されます。行末のホワイトスペース(改行など)は取り除かれます。配列にすでに要素が入っている場合は、その後ろに追記されます。 - 第三引数
$result_code
を指定すると、コマンドの終了ステータスが格納されます。終了ステータスは、正常終了ならば0
、異常終了ならば0
以外になります。 - 戻り値は、コマンドの実行結果の最後の行です。失敗時は
false
を返します。
exec()関数の使い方
まずls -l sample_dir
というコマンドを実行して、戻り値を出力してみます。これはsample_dir
内のファイル一覧を表示するコマンドです。
$command = 'ls -l sample_dir';
echo exec($command);
/*実行結果
drwxr-xr-x 2 masawo staff 64 6 14 01:04 sample3
*/
これでは、実行結果の最後の1行しか表示できません。
次は、第二引数を指定してすべての結果を取得する例です。
$command = 'ls -l sample_dir';
exec($command, $output);
print_r($output);
/*実行結果
Array
(
[0] => total 16
[1] => -rw-r--r-- 1 masawo staff 209 6 14 01:05 sample1.php
[2] => -rw-r--r-- 1 masawo staff 1367 6 14 01:10 sample2.txt
[3] => drwxr-xr-x 2 masawo staff 64 6 14 01:04 sample3
)
*/
実行結果の各行が$output
の要素として格納されました。いえーい。
さらに、終了ステータスを取得してみましょう。
$command = 'ls -l sample_dir';
exec($command, $output, $result_code);
echo $result_code;
/*実行結果
0
*/
$command = 'ls -l nonexistent_dir'; //存在しないディレクトリを参照
exec($command, $output, $result_code);
echo $result_code;
/*実行結果
1
*/
コマンドの実行に成功した場合は0
、失敗した場合は1
が出力されました。いえーい。
終了ステータスで条件分岐をして、エラーメッセージを表示したりできますね。
$command = 'ls -l sample_dir';
exec($command, $output, $result_code);
//終了ステータスが0以外ならばエラー
if ($result_code) {
echo 'コマンドの実行に失敗しました';
//終了ステータスが0ならば成功
} else {
print_r($output);
}
無料で利用できるプログラミング学習サービスをお探しならば Code Lesson はいかがでしょうか。プロのエンジニアが監修した学習ロードマップで効率的に学習、AIに質問、最後にクイズで理解度をチェックできます。
passthru()
passthru()関数の書式
passthru($command, $result_code)
- 第一引数
$command
は、実行するコマンドを指定します。 - 第二引数
$result_code
を指定すると、コマンドの終了ステータスが格納されます。終了ステータスは、正常終了ならば0
、異常終了ならば0
以外になります。 - 戻り値は、成功時に
null
、失敗時にfalse
を返します。 - コールすると実行結果をそのまま出力します。
passthru()関数の使い方
まずはls -l sample_dir
を実行してみましょう。
$command = 'ls -l sample_dir';
passthru($command);
/*実行結果
total 16
-rw-r--r-- 1 masawo staff 209 6 14 01:05 sample1.php
-rw-r--r-- 1 masawo staff 1367 6 14 01:10 sample2.txt
drwxr-xr-x 2 masawo staff 64 6 14 01:04 sample3
*/
コマンドの実行結果が、何も加工されずにそのまま表示されました。exec()
関数より簡単じゃんと思ったかもしれませんが、このpassthru()
関数は実行結果をただ出力するだけです。変数に入れておいてゴニョゴニョするということはできませんのでご注意ください。
passthru()
関数が実力を発揮するのは、バイナリデータを扱うときです。PNG画像を表示する例を見てみましょう。
header('Content-Type: image/png');
$command = 'cat image.png';
passthru($command);
/*実行結果
(画像が表示されます)
*/
最初にheader()
関数を使ってHTTPヘッダを送信します。ブラウザに画像を表示するには、画像データを出力する前にContent-Typeを送信しておく必要があります。次にcat image.png
コマンドを実行して画像ファイルのデータをそのまま出力しています。
passthru()の出力を変数に入れたい
passthru()
は実行結果をブラウザなどに直接出力する関数です。そうではなく、変数に格納したいというワガママなあなたにPHPの出力制御関数を使う方法を伝授します。
//出力のバッファリングを開始する
ob_start();
//コマンドの実行
$command = 'cat image.png';
passthru($command);
//バッファの内容を変数に格納
$output = ob_get_contents();
//バッファをクリアしバッファリングを終了する
ob_end_clean();
バッファリングとは、出力を一旦バッファに溜めておくことです。頭痛薬ではありません(つまらない)。この例ではpassthru()
関数の出力をすぐに表示せずにバッファリングして、そのデータをob_get_contents()
関数を使って変数$output
に格納しています。
system()
system()関数の書式
system($command, $result_code)
- 第一引数
$command
は、実行するコマンドを指定します。 - 第二引数
$result_code
を指定すると、コマンドの終了ステータスが格納されます。終了ステータスは、正常終了ならば0
、異常終了ならば0
以外になります。 - 戻り値は、コマンドの実行結果の最後の行です。失敗時は
false
を返します。 - コールすると実行結果をそのまま出力します。
system()関数の使い方
いつものls -l sample_dir
コマンドを実行してみます。
$command = 'ls -l sample_dir';
system($command);
/*実行結果
total 16
-rw-r--r-- 1 masawo staff 209 6 14 01:05 sample1.php
-rw-r--r-- 1 masawo staff 1367 6 14 01:10 sample2.txt
drwxr-xr-x 2 masawo staff 64 6 14 01:04 sample3
*/
実行結果がすべて表示されました。
exec()
関数と比較すると、実行結果をただ表示するだけならばsystem()
関数のほうが簡単なんですけど、それだけなんですよね。ケースバイケースで使い分けるほどの魅力を感じないです(辛口)。
shell_exec()
shell_exec()関数の書式
shell_exec($command)
- 引数
$command
は、実行するコマンドを指定します。 - 戻り値は、コマンドの実行結果です。失敗時は
null
を返します。コマンドが何も出力しなかった場合もnull
を返します。
shell_exec()関数の使い方
いつものls -l sample_dir
コマンドを実行してみましょう。
$command = 'ls -l sample_dir';
$output = shell_exec($command);
echo $output;
/*実行結果
total 16
-rw-r--r-- 1 masawo staff 209 6 14 01:05 sample1.php
-rw-r--r-- 1 masawo staff 1367 6 14 01:10 sample2.txt
drwxr-xr-x 2 masawo staff 64 6 14 01:04 sample3
*/
複数行の実行結果を取得して変数に入れることができます。終了ステータスの取得ができないのが欠点です。shell_exec()
関数は、次に解説する実行演算子のエイリアスです。だから関数としてはとてもシンプルだけど機能面で物足りないんですね。
実行演算子
実行演算子の書式
$output = `$command`;
コマンドをバッククォート`...`
で括ると、その実行結果を返します。
実行演算子の使い方
例のls -l sample_dir
コマンドを実行してみましょう。
$output = `ls -l sample_dir`;
echo $output;
/*実行結果
total 16
-rw-r--r-- 1 masawo staff 209 6 14 01:05 sample1.php
-rw-r--r-- 1 masawo staff 1367 6 14 01:10 sample2.txt
drwxr-xr-x 2 masawo staff 64 6 14 01:04 sample3
*/
とてもシンプルに書くことができます。シンプルすぎてコードを読む時にわかりにくいので、私は使いません。老眼だと、バッククォートとシングルクォートの区別がつかないのよね。
標準エラー出力を取得したい
例えば、ターミナルで次のようなコマンドを実行するとエラーメッセージが表示されます。
% ls -l nonexistent_dir #存在しないディレクトリを参照
ls: nonexistent_dir: No such file or directory
これまで紹介した5つの方法では、このエラーメッセージを取得することができません。これは、これらの関数が取得する実行結果は標準出力(stdout)のみだからです。
コマンドの実行に成功したときの出力を標準出力(stdout)、失敗したときの出力を標準エラー出力(stderr)といいます。標準エラー出力を取得するには、コマンドの最後に2>&1
という呪文を追加します。
$command = 'ls -l nonexistent_dir 2>&1';
exec($command, $output);
print_r($output);
/*実行結果
Array
(
[0] => ls: nonexistent_dir: No such file or directory
)
*/
2>&1
は、標準出力と標準エラー出力をまとめて出力するための呪文です。なので、成功すれば実行結果が出力されるし、失敗すればエラーメッセージが出力されます。
さいごに
というわけで、PHPで外部コマンドを実行する方法を比較しました。もう一度いいますが、
- コマンドの出力がテキストならば
exec()
関数 - コマンドの出力がバイナリならば
passthru()
関数
です。他の方法は使いません。覚えなくていいです。私を信じてください。
でわでわ
プログラミングの独学に行き詰まっていませんか?誰かに相談したい、もっと効率よく学びたいなら、プログラミングスクールを検討してみてください。
コメント