開発環境・コマンドライン

PHPで外部コマンドを実行する方法について解説

PHPでコマンドを実行する方法は、CLI環境での自動化やタスク管理に役立ちます。

この記事では、PHPの組み込み関数(たとえば、exec()shell_exec())を利用して外部コマンドを実行する基本的な手法や注意点を分かりやすく解説します。

初心者でも理解しやすい具体例を交えながら、実務に活かせる知識を提供します。

PHPコマンド実行に利用される関数

exec()の利用方法

基本的な使い方

exec()は、外部コマンドを実行してその結果を配列として受け取る関数です。

シンプルなコマンド実行に対して利用しやすい点が特徴です。

以下にシンプルな例を示します。

<?php
// コマンド出力を格納する変数
$output = array();
// 'ls -l'コマンドを実行し、結果を$outputに格納する
exec('ls -l', $output, $status);
// 結果を表示する
print_r($output);
?>
Array
(
    [0] => total 12
    [1] => -rw-r--r-- 1 user group 100 Oct 10 12:34 file1.txt
    [2] => -rw-r--r-- 1 user group 120 Oct 10 12:35 file2.txt
)

出力結果と戻り値の取り扱い

exec()では、出力結果が配列に格納され、変数$statusにコマンドの終了ステータスが返されます。

通常、ステータスが0であれば正常終了を意味します。

以下の例では、終了ステータスによって処理を分けています。

<?php
$output = array();
// 'ls -l'コマンドを実行
exec('ls -l', $output, $status);
// 正常終了の場合の処理
if ($status === 0) {
    echo "コマンドは正常に実行されました。\n";
} else {
    // エラー時の処理
    echo "エラーが発生しました。終了ステータス: " . $status;
}
?>
コマンドは正常に実行されました。

shell_exec()でのコマンド実行

利用シーンと特徴

shell_exec()は、コマンドの全体の出力を文字列として返すため、複数行の出力を1つの文字列で受け取る場合に便利です。

シンプルなスクリプトで出力全体を利用するシーンで有用です。

結果取得の方法

以下の例では、shell_exec()を用いてコマンド実行後の出力結果を文字列として受け取り、そのまま表示しています。

<?php
// 'ls -l'コマンドを実行し、全体の出力を文字列として取得
$commandOutput = shell_exec('ls -l');
echo $commandOutput;
?>
total 12
-rw-r--r-- 1 user group 100 Oct 10 12:34 file1.txt
-rw-r--r-- 1 user group 120 Oct 10 12:35 file2.txt

system()とpassthru()の使い分け

実行結果の表示と処理

system()passthru()は、実行結果をリアルタイムで画面に出力する場合に利用されます。

system()は実行結果を変数に格納できる点が特徴で、passthru()はバイナリデータなどの扱いに適しています。

下記の例では、system()を利用して結果を出力し、終了ステータスも取得しています。

<?php
// system()を使用してコマンド実行と同時に結果を画面に出力
$status = system('ls -l', $returnStatus);
echo "\n終了ステータス: " . $returnStatus;
?>
total 12
-rw-r--r-- 1 user group 100 Oct 10 12:34 file1.txt
-rw-r--r-- 1 user group 120 Oct 10 12:35 file2.txt
終了ステータス: 0

エラーチェックのポイント

system()passthru()使用時には、終了ステータスの確認が重要です。

実行中にエラーが発生した場合、そのステータスをもとにエラー対策を行うとよいでしょう。

次の例は、無効なコマンド実行時にエラー処理を行っています。

<?php
// 無効なコマンドを実行し、終了ステータスで失敗を確認する
$returnStatus = -1;
system('invalidCommand', $returnStatus);
if ($returnStatus !== 0) {
    echo "コマンド実行に失敗しました。ステータス: " . $returnStatus;
}
?>
コマンド実行に失敗しました。ステータス: 127

コマンド実行結果の取得と管理

出力結果の取得方法

結果を変数へ格納する方法

コマンド実行結果を文字列として変数に格納したい場合、exec()shell_exec()が利用できます。

各行を配列で受け取り、必要に応じて文字列に変換する方法もあります。

<?php
$output = array();
// exec()を利用して'ls -l'コマンドを実行
exec('ls -l', $output);
$result = implode("\n", $output); // 配列の各要素を連結して1つの文字列に変換する
echo $result;
?>
total 12
-rw-r--r-- 1 user group 100 Oct 10 12:34 file1.txt
-rw-r--r-- 1 user group 120 Oct 10 12:35 file2.txt

配列での取得方法

exec()はコマンド出力を配列で取得できるため、各行を個別に処理する際に有効です。

以下の例では、出力結果を1行ずつループで表示しています。

<?php
$output = array();
// exec()で出力結果を配列に格納する
exec('ls -l', $output);
// 配列の各要素をループで出力する
foreach ($output as $line) {
    echo $line . "\n";
}
?>
total 12
-rw-r--r-- 1 user group 100 Oct 10 12:34 file1.txt
-rw-r--r-- 1 user group 120 Oct 10 12:35 file2.txt

戻り値の確認とエラー処理

コマンド終了ステータスの確認

外部コマンド実行時は、終了ステータスで実行結果を確認することができます。

通常、ステータスが0の場合は正常に終了したと判断されます。

下記の例では、終了ステータスをチェックして処理を分岐しています。

<?php
$output = array();
exec('ls -l', $output, $status);
if ($status === 0) {
    echo "コマンドは正常に終了しました。\n";
} else {
    echo "エラーが発生しました。終了ステータス\( $status \)です。\n";
}
?>
コマンドは正常に終了しました。

失敗時の対処法

コマンド実行に失敗した場合は、エラー情報をログに記録する、またはユーザーにエラーメッセージを表示するなどの対策が必要です。

次の例は、失敗時のエラー処理の一例です。

<?php
$output = array();
exec('invalidCommand', $output, $status);
if ($status !== 0) {
    // エラー情報をログへ出力する
    error_log("コマンド実行に失敗しました。ステータス: " . $status);
    echo "エラーが発生しました。詳細はログを確認してください。\n";
}
?>
エラーが発生しました。詳細はログを確認してください。

セキュリティ対策とリスク管理

入力値の検証とサニタイズ

安全なコマンド組み立て方法

外部からの入力をコマンドに組み込む際は、危険な文字を除去するなどのサニタイズ処理が必要です。

以下の例は、escapeshellarg()を利用して安全な形でコマンドを組み立てる方法です。

<?php
// 外部から受け取った入力値
$userInput = $_GET['filename'];
// 危険な文字を除去するためにエスケープ処理を実施
$sanitizedInput = escapeshellarg($userInput);
// 安全なコマンドを組み立てる
$command = 'ls -l ' . $sanitizedInput;
exec($command, $output, $status);
echo implode("\n", $output);
?>
-rw-r--r-- 1 user group 100 Oct 10 12:34 safe_file.txt

PHPフィルタ機能の活用

PHPのフィルタ機能を用いて、入力値が正しい形式であるかを確認することも重要です。

下記のコードは、入力値をフィルタで検証し、適切な入力のみを利用する例です。

<?php
// 外部から受け取った入力値
$userInput = $_GET['filename'];
// FILTER_SANITIZE_STRINGで入力値のサニタイズを実施
if (filter_var($userInput, FILTER_SANITIZE_STRING) === false) {
    echo "無効な入力値です。";
    exit;
}
// さらにエスケープ処理を行い、安全なコマンドを組み立てる
$command = 'ls -l ' . escapeshellarg($userInput);
exec($command, $output, $status);
echo implode("\n", $output);
?>
-rw-r--r-- 1 user group 100 Oct 10 12:34 file.txt

シェルインジェクション対策

リスク回避の方法

シェルインジェクション攻撃を防ぐためには、外部からの入力値をそのままコマンドに渡さないよう注意が必要です。

escapeshellarg()escapeshellcmd()を適切に利用して、入力値のエスケープ処理を徹底してください。

以下はその一例です。

<?php
// ユーザーからの入力値を受け取る
$userInput = $_GET['arg'];
// 入力値をシェル上で安全に扱うためのエスケープ
$safeArg = escapeshellarg($userInput);
// 安全にコマンドを構築する
$command = 'someCommand ' . $safeArg;
exec($command, $output, $status);
echo implode("\n", $output);
?>
someCommand実行結果...

外部コマンド実行時の注意点

外部コマンドを実行する際は、コマンドのパスや実行権限を十分に確認する必要があります。

不要な権限での実行を避け、ホワイトリスト方式で実行可能なコマンドを限定することも有用です。

以下はその例です。

<?php
// 実行が許可されたコマンドのホワイトリストを定義する
$allowedCommands = array('ls', 'cat', 'echo');
// ユーザーからコマンド名を受け取る
$commandInput = $_GET['cmd'];
if (in_array($commandInput, $allowedCommands)) {
    // 安全なコマンドとして実行する
    $command = escapeshellcmd($commandInput) . ' -l';
    system($command);
} else {
    echo "許可されていないコマンドです。";
}
?>
total 12
-rw-r--r-- 1 user group 100 Oct 10 12:34 file1.txt
-rw-r--r-- 1 user group 120 Oct 10 12:35 file2.txt

同期実行と非同期実行の違い

同期実行の特徴

同期実行では、外部コマンドの実行が完了するまで次の処理に移らず、結果の取得やエラーチェックが確実に行えます。

ただし、実行に時間がかかる場合、全体の処理時間に影響を及ぼす点に注意が必要です。

実行タイミング管理のポイント

以下は、同期実行でexec()を利用し、コマンドの実行が完了するまで待機する例です。

<?php
$output = array();
// 'sleep 2'で2秒待機後に'ls -l'コマンドを実行する例
exec('sleep 2 && ls -l', $output, $status);
echo implode("\n", $output);
?>
total 12
-rw-r--r-- 1 user group 100 Oct 10 12:34 file1.txt
-rw-r--r-- 1 user group 120 Oct 10 12:35 file2.txt

非同期実行の実装例

バックグラウンド実行の方法

非同期実行では、コマンドの実行結果を待たずに次の処理へ移るため、処理速度を向上させることができます。

UNIX系システムでは、コマンド末尾に&を付けることでバックグラウンド実行が可能です。

以下にサンプルコードを示します。

<?php
// バックグラウンドでコマンドを実行し、結果を一時ファイルに出力する例
$command = 'sleep 2 && ls -l > /tmp/output.txt &';
exec($command);
echo "コマンドをバックグラウンドで実行中です。\n";
?>
コマンドをバックグラウンドで実行中です。

リソース管理の注意点

非同期実行では、実行中のプロセスが残る場合があるため、定期的なプロセスの監視やリソース管理が必要となります。

次の例は、実行中のプロセス状況を確認する基本的な方法です。

<?php
$output = array();
// 現在実行中のプロセス情報を取得する
exec('ps aux | grep someProcess', $output);
echo implode("\n", $output);
?>
user 1234 0.0 0.1 123456 1234 ? S 12:34 0:00 someProcess

まとめ

本記事では、PHPにおける外部コマンド実行の関数(exec(), shell_exec(), system(), passthru())の基本的な使い方や、出力結果・戻り値の管理、エラー処理、セキュリティ対策、さらには同期・非同期実行の違いについて詳しく解説しました。

記事を通して各関数の特徴と実践的な使い分け方法が理解でき、状況に応じた最適な選択が行えるようになりました。

ぜひ、実際の開発でこれらの手法を試して、効率的なシステム構築を目指してください。

関連記事

Back to top button
目次へ