【PHP8.x】exec()関数の使い方
exec関数の使い方について、初心者にもわかりやすく解説します。
基本的な使い方
exec関数は、外部プログラムを実行する関数です。この関数は、PHPスクリプトからオペレーティングシステムのコマンドライン上で実行可能なプログラムを呼び出し、その結果を文字列として取得するために使用されます。exec関数は、セキュリティ上のリスクを伴う可能性があるため、使用には十分な注意が必要です。
具体的には、exec関数は指定されたコマンドを実行し、そのコマンドの標準出力(stdout)を変数に格納することができます。オプションで、コマンドの終了ステータス(リターンコード)を取得することも可能です。これにより、実行された外部プログラムが正常に終了したかどうかを判断できます。
exec関数の基本的な構文は以下の通りです。
string exec ( string $command [, array &$output [, int &$return_var ]] )
ここで、$commandは実行するコマンド、$outputはコマンドの出力が格納される配列(オプション)、$return_varはコマンドの終了ステータスが格納される変数(オプション)です。
exec関数を使用する際には、以下の点に注意する必要があります。
- セキュリティ: ユーザーからの入力など、信頼できないデータに基づいてコマンドを生成することは避けるべきです。コマンドインジェクション攻撃のリスクがあります。入力値を適切に検証し、エスケープ処理を行う必要があります。
- 権限: PHPスクリプトを実行しているユーザー(通常はWebサーバーのユーザー)が、実行しようとしているコマンドに対する実行権限を持っている必要があります。
- 出力の扱い: コマンドの出力は、大量のデータを返す可能性があります。必要に応じて、出力を制限したり、適切に処理したりする必要があります。
- エラー処理: コマンドが正常に実行されなかった場合(例えば、ファイルが見つからない、権限がないなど)、適切なエラー処理を行う必要があります。
exec関数は、システム管理ツールや外部サービスとの連携など、特定の状況下では非常に有用な機能です。しかし、セキュリティと権限に注意し、慎重に使用する必要があります。より安全な代替手段として、proc_open関数やshell_exec関数なども検討できます。
構文(syntax)
1<?php 2$output = []; 3exec('ls -l', $output); 4print_r($output); 5?>
引数(parameters)
string $command, array &$output = null, int &$result_code = null
- string $command: 実行したいコマンドライン文字列
- array &$output = null: コマンドの実行結果の各行を格納する配列(参照渡し)
- int &$result_code = null: コマンドの終了コードを格納する整数(参照渡し)
戻り値(return)
string|false
exec関数は、実行したコマンドの標準出力を文字列として返します。コマンドの実行に失敗した場合はfalseを返します。
サンプルコード
PHPのexec()とshell_exec()の違いを理解する
1<?php 2 3/** 4 * PHPのexec()とshell_exec()関数の違いを示すサンプルコードです。 5 * 6 * exec(): 7 * - コマンドの最終行の出力のみを文字列として直接返します。 8 * - コマンドの全ての出力は、第2引数で渡された配列(参照渡し)に各行が要素として格納されます。 9 * - コマンドの終了コードは、第3引数で渡された整数変数(参照渡し)に格納されます。 10 * - 失敗した場合、falseを返します。 11 * 12 * shell_exec(): 13 * - コマンドの全ての出力を単一の文字列として返します。 14 * - 終了コードを取得するメカニズムは組み込まれていません。 15 * - 失敗した場合、nullを返します。 16 */ 17function demonstrateShellExecutionDifferences(): void 18{ 19 // 実行するシェルコマンドの例 20 // 複数行の出力を生成するコマンドを使用することで、exec()とshell_exec()の違いが明確になります。 21 $command = 'echo "--- output from shell command ---"; echo "First line."; echo "Second line."; echo "Last line.";'; 22 23 echo "--- exec() の使用例 ---\n"; 24 25 $outputExec = []; // コマンドの全出力を格納する配列 26 $resultCodeExec = 0; // コマンドの終了コードを格納する変数 27 28 // exec() を実行 29 // 戻り値はコマンドの最終行の出力、または失敗時に false 30 $lastLineExec = exec($command, $outputExec, $resultCodeExec); 31 32 if ($lastLineExec === false) { 33 echo "exec() コマンドの実行に失敗しました。\n"; 34 } else { 35 echo "exec() の直接の戻り値 (最終行): " . ($lastLineExec === '' ? '(空行)' : $lastLineExec) . "\n"; 36 echo "exec() の全出力 (配列):\n"; 37 foreach ($outputExec as $line) { 38 echo " - " . $line . "\n"; 39 } 40 echo "exec() の終了コード: " . $resultCodeExec . "\n"; 41 } 42 43 echo "\n--- shell_exec() の使用例 ---\n"; 44 45 // shell_exec() を実行 46 // 戻り値はコマンドの全ての出力 (文字列)、または失敗時に null 47 $allOutputShell = shell_exec($command); 48 49 if ($allOutputShell === null) { 50 echo "shell_exec() コマンドの実行に失敗しました。\n"; 51 } else { 52 echo "shell_exec() の全出力 (文字列):\n"; 53 echo $allOutputShell; // shell_exec は全ての出力をそのまま文字列として返すため、改行も含まれます。 54 } 55 56 echo "\n--- まとめ ---\n"; 57 echo "exec(): 最終行を直接返し、全出力は配列で、終了コードも取得可能です。\n"; 58 echo "shell_exec(): 全出力を単一の文字列で返します。終了コードは取得できません。\n"; 59} 60 61// 関数を実行して、exec() と shell_exec() の動作の違いを確認します。 62demonstrateShellExecutionDifferences();
PHPのexec()関数とshell_exec()関数は、PHPでシェルコマンドを実行する際に使われます。出力の取得方法とエラーハンドリングに違いがあります。
exec()関数は、実行したコマンドの出力の「最終行のみ」を文字列として直接返します。第2引数に参照渡しで指定した配列には、コマンドのすべての出力が各行で格納されます。また、第3引数に参照渡しで指定した整数変数には、コマンドの終了コードが格納され、成否を確認できます。失敗時はfalseを返します。
一方、shell_exec()関数は、実行したコマンドの「すべての出力」を単一の文字列として返します。コマンドの終了コードは直接取得できません。失敗時はnullを返します。
そのため、コマンドのすべての出力を細かく処理したり、終了コードを確認してエラー処理を行いたい場合はexec()が便利です。単にコマンドの出力全体を文字列として取得したいだけであればshell_exec()が簡潔に利用できます。
PHPのexec()とshell_exec()は、どちらもシェルコマンドを実行しますが、情報の取得方法が異なります。exec()はコマンドの最終行を直接返し、全出力は第2引数の配列で、終了コードは第3引数で取得できるため、詳細な処理結果やエラーハンドリングに適しています。対してshell_exec()は、コマンドの全出力を一つの文字列として返しますが、終了コードを直接取得する手段がありません。
どちらの関数も、実行するコマンドに外部からの入力値を直接含めると、意図しないコマンドが実行されるOSコマンドインジェクションのリスクがあります。コマンドを安全に利用するためには、必ずescapeshellarg()やescapeshellcmd()などのエスケープ関数を使って、入力値を適切に処理してください。これにより、セキュリティ上の問題を防ぎ、システムを安全に保つことができます。
PHPでコマンドを実行し結果を取得する
1<?php 2 3/** 4 * 指定されたコマンドを実行し、その結果、終了コード、出力の最終行を返します。 5 * 6 * @param string $command 実行するシェルコマンド文字列。 7 * @return array 実行結果を含む連想配列。 8 * - 'success': bool コマンドの実行に成功したか (execがfalseを返さなかったか)。 9 * - 'output': array コマンドの各出力行を含む配列。 10 * - 'last_line': string|false コマンド出力の最終行、または失敗時はfalse。 11 * - 'result_code': int|null コマンドの終了コード。成功時は0、失敗時は0以外の整数、実行前はnull。 12 */ 13function executeSystemCommand(string $command): array 14{ 15 // コマンドの全ての出力行を格納する配列を初期化 16 $outputLines = []; 17 // コマンドの終了コードを格納する変数を初期化 18 $resultCode = null; 19 20 // exec関数は、指定されたコマンドを実行し、その出力の最終行を返します。 21 // 第2引数(&$outputLines)には、コマンドの全ての出力行が配列として格納されます。 22 // 第3引数(&$resultCode)には、コマンドの終了コードが整数として格納されます。 23 // コマンドの実行に失敗した場合、execはfalseを返します。 24 $lastLine = exec($command, $outputLines, $resultCode); 25 26 // execがfalseを返さなければ、コマンドの実行自体は成功したと判断 27 $success = ($lastLine !== false); 28 29 return [ 30 'success' => $success, 31 'output' => $outputLines, 32 'last_line' => $lastLine, 33 'result_code' => $resultCode, 34 ]; 35} 36 37// --- 使用例 --- 38 39echo "--- 成功するコマンドの実行例 ---" . PHP_EOL; 40 41// 実行するコマンドはOSによって切り替える(例: ディレクトリの内容一覧表示) 42// Windowsの場合は 'dir'、Linux/macOSの場合は 'ls -l' 43$successfulCommand = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? 'dir' : 'ls -l'; 44$result = executeSystemCommand($successfulCommand); 45 46if ($result['success']) { 47 echo "コマンド '{$successfulCommand}' の実行に成功しました。" . PHP_EOL; 48 echo "終了コード: " . ($result['result_code'] ?? 'N/A') . PHP_EOL; 49 echo "最終出力行: " . ($result['last_line'] !== false ? "'{$result['last_line']}'" : "なし") . PHP_EOL; 50 echo "--- 全出力 ---" . PHP_EOL; 51 foreach ($result['output'] as $line) { 52 echo " " . $line . PHP_EOL; 53 } 54} else { 55 echo "コマンド '{$successfulCommand}' の実行に失敗しました。" . PHP_EOL; 56 echo "終了コード: " . ($result['result_code'] ?? 'N/A') . PHP_EOL; 57 echo "エラーメッセージ: exec関数が false を返しました。" . PHP_EOL; 58} 59 60echo PHP_EOL . "--- 存在しないコマンドの実行例 ---" . PHP_EOL; 61 62$failedCommand = 'this_command_does_not_exist_12345'; // 通常は存在しないであろうコマンド 63$result = executeSystemCommand($failedCommand); 64 65if ($result['success']) { 66 echo "コマンド '{$failedCommand}' の実行に成功しました。" . PHP_EOL; 67 echo "終了コード: " . ($result['result_code'] ?? 'N/A') . PHP_EOL; 68 echo "最終出力行: " . ($result['last_line'] !== false ? "'{$result['last_line']}'" : "なし") . PHP_EOL; 69 echo "--- 全出力 ---" . PHP_EOL; 70 foreach ($result['output'] as $line) { 71 echo " " . $line . PHP_EOL; 72 } 73 // 終了コードが0以外の場合、通常は失敗とみなされる 74 if ($result['result_code'] !== 0) { 75 echo "(コマンドは実行されましたが、終了コードが0ではないため、問題があった可能性があります。)" . PHP_EOL; 76 } 77} else { 78 echo "コマンド '{$failedCommand}' の実行に失敗しました。" . PHP_EOL; 79 echo "終了コード: " . ($result['result_code'] ?? 'N/A') . PHP_EOL; 80 echo "エラーメッセージ: exec関数が false を返しました (コマンドが見つからないか、実行権限がありません)。" . PHP_EOL; 81}
PHPのexec関数は、OS(オペレーティングシステム)上で外部コマンドを実行するための機能で、PHP 8で利用できます。システムエンジニアがシェルコマンドなどをPHPスクリプトから実行する際によく使用されます。
この関数は第一引数に実行したいコマンド文字列(例: 'ls -l'や'dir')を受け取ります。第二引数には、コマンドが実行された際に出力される全ての行を配列として格納するための変数(参照渡し)を指定します。また、第三引数には、コマンドの終了コード(通常、成功は0、失敗は0以外の値)を格納するための変数(参照渡し)を指定できます。
exec関数自体の戻り値は、コマンド出力の最終行の文字列です。もしコマンドの実行に失敗した場合、この関数はfalseを返します。サンプルコードでは、このexec関数を使ってコマンドを実行し、その出力、終了コード、そして実行の成否をまとめて取得するexecuteSystemCommandというヘルパー関数を作成しています。これにより、複雑なシェルコマンドの実行結果をより扱いやすくしています。
PHPのexec関数は、コマンドの最終出力行を直接返しますが、すべての出力行は第2引数で、コマンドの終了コードは第3引数で受け取ります。execがfalseを返した場合、コマンドが見つからないなど実行自体に失敗しています。しかし、実行に成功しても、終了コードが0以外であればコマンド内部で問題が発生している可能性があるため、必ず終了コードも確認してください。特に注意すべきは、ユーザーからの入力値をコマンドに直接含めると、システムを危険に晒す「コマンドインジェクション」のリスクがある点です。安全のため、入力値は厳しく検証・エスケープするか、この関数は安易に利用しないようにしましょう。また、OSによって実行可能なコマンドが異なる点も考慮が必要です。
PHP exec コマンド実行とデバッグ
1<?php 2 3/** 4 * 外部コマンドを実行し、その詳細な結果と、 5 * コマンドが「実行されない」場合に考えられる原因のヒントを出力します。 6 * 7 * システムエンジニアを目指す初心者向けに、exec関数の使い方とデバッグ方法を示します。 8 * 9 * @param string $command 実行するコマンド文字列。 10 * @return void 11 */ 12function runExternalCommandWithDebug(string $command): void 13{ 14 echo "--- コマンド実行テスト: '{$command}' ---\n"; 15 16 // コマンドの全出力行が格納される配列を初期化 17 $outputLines = []; 18 // コマンドの終了コードが格納される変数を初期化 (通常、成功は0、失敗は非0) 19 $returnCode = 0; 20 21 // exec関数を実行。 22 // 第1引数: 実行するコマンド。 23 // 第2引数: コマンドの全出力行が配列として格納される (参照渡し)。 24 // 第3引数: コマンドの終了コードが整数として格納される (参照渡し)。 25 // 戻り値: コマンド出力の最終行、またはexec関数が実行できない場合は false。 26 $lastLine = exec($command, $outputLines, $returnCode); 27 28 // exec関数自体が実行できない場合(例: php.iniでdisable_functionsに登録されている) 29 if ($lastLine === false) { 30 echo "エラー: PHPのexec関数が実行できませんでした。\n"; 31 echo " - 考えられる原因:\n"; 32 echo " 1. PHPの設定ファイル (php.ini) で 'disable_functions' に exec が含まれていないか確認してください。\n"; 33 echo " 2. サーバーのセキュリティポリシーにより、外部コマンド実行が完全に制限されている可能性があります。\n"; 34 echo " 3. PHPの実行環境に致命的な問題がある可能性があります。\n"; 35 echo " - 解決策: php.iniを確認し、必要であればサーバー管理者に連絡してください。\n"; 36 return; // 以降の処理はスキップ 37 } 38 39 echo "--- 実行結果詳細 ---\n"; 40 echo " - コマンド出力の最終行 (lastLine): " . ($lastLine === '' ? '(出力なし)' : $lastLine) . "\n"; 41 echo " - コマンドの終了コード (returnCode): {$returnCode}\n"; 42 43 echo " - 全てのコマンド出力 (outputLines配列):\n"; 44 if (empty($outputLines)) { 45 echo " (出力なし)\n"; 46 } else { 47 foreach ($outputLines as $line) { 48 echo " > {$line}\n"; 49 } 50 } 51 52 // コマンドの終了コードが0以外の場合、コマンドが正常に終了しなかったことを意味します。 53 if ($returnCode !== 0) { 54 echo "--- 注意: コマンドはエラー終了しました (終了コード: {$returnCode}) ---\n"; 55 echo " - 考えられる原因:\n"; 56 echo " 1. コマンドのパスが正しくない、またはコマンドが存在しない可能性があります。(例: '/usr/bin/ls' のようにフルパスを指定してみてください)\n"; 57 echo " 2. コマンドのスペルミス、または引数が間違っている可能性があります。\n"; 58 echo " 3. PHPプロセスを実行しているユーザーに、コマンドの実行権限がない可能性があります。\n"; 59 echo " 4. コマンド自体がエラーメッセージを出力している場合、上記の '全てのコマンド出力' でエラー内容を確認できます。\n"; 60 } else { 61 echo "--- コマンドは正常に終了しました。---\n"; 62 } 63 echo "\n"; 64} 65 66// --- 実際のコマンド実行例 --- 67 68// 1. 正常に動作するコマンドの例 (現在ディレクトリのファイル一覧表示) 69// Linux/macOSの場合: 'ls -al' 70// Windowsの場合: 'dir' 71runExternalCommandWithDebug('ls -al'); 72// runExternalCommandWithDebug('dir'); // Windows環境でテストする場合 73 74// 2. 存在しないコマンドの例 (エラー終了し、returnCodeが非ゼロになることを確認) 75runExternalCommandWithDebug('this_command_does_not_exist_xyz'); 76 77// 3. エラーメッセージを出力するが、実行自体は可能なコマンドの例 (例: lsに無効なオプション) 78runExternalCommandWithDebug('ls -z'); // '-z' は 'ls' コマンドの無効なオプション 79 80// 4. 標準出力を伴うシンプルなコマンドの例 81runExternalCommandWithDebug('echo "Hello from PHP exec!"'); 82 83// セキュリティに関する注意: 84// ユーザーからの入力に基づいてコマンドを構築する場合、コマンドインジェクションのリスクがあります。 85// そのような場合は、必ず `escapeshellarg()` や `escapeshellcmd()` を使用して 86// コマンド引数を適切にエスケープしてください。
PHPのexec関数は、サーバー上で外部コマンドを実行するために使用します。この関数は、PHPスクリプトからOSのコマンドライン操作を行いたい場合に便利です。
第1引数には、実行したいコマンドを文字列で指定します。例えば、'ls -al'のように指定すると、ファイル一覧を取得するコマンドが実行されます。第2引数に配列変数を渡すと、コマンドの出力結果が1行ずつその配列に格納されます。第3引数に変数を渡すと、コマンドの終了コード(リターンステータス)が格納されます。終了コードは、コマンドが正常に成功したか(通常は0)、エラーで終了したか(0以外)を判断するための重要な指標です。
exec関数の戻り値は、コマンドが出力した内容の最終行のみを文字列として返します。もしPHPの設定でexec関数自体が無効化されているなど、コマンドを実行できなかった場合はfalseが返ります。
このサンプルコードは、これらの引数と戻り値をすべて活用し、コマンドが「実行されない」場合に考えられる原因をデバッグする方法を示しています。戻り値がfalseになるか、第3引数の終了コードが0以外になるかを確認することで、PHPの設定に問題があるのか、それとも実行しようとしたコマンド側に問題があるのかを効率的に切り分けることができます。
exec関数が期待通りに動作しない場合、PHPの設定で無効化されているケースと、実行したコマンド自体が失敗しているケースの切り分けが重要です。関数の戻り値がfalseならPHP側の設定を、第3引数の終了コードが0以外ならコマンド側の問題を疑います。コマンド側のエラーでは、パスが通っていない、実行権限がないといった原因が考えられます。コマンドのエラー出力を確認したい場合は、コマンドの末尾に 2>&1 を追記し、エラーを標準出力にリダイレクトさせる手法が有効です。また、ユーザーが入力した値をコマンドに使う際は、escapeshellarg関数で必ずエスケープ処理を行い、コマンドインジェクションという深刻な脆弱性を防ぐ必要があります。
PHP exec でコマンドを非同期実行する
1<?php 2 3/** 4 * コマンドを非同期(バックグラウンド)で実行します。 5 * 6 * PHPのexec関数は通常、指定したコマンドが終了するまで処理をブロックしますが、 7 * コマンドの末尾に特定の記号を追加することで、コマンドの終了を待たずに 8 * PHPスクリプトの処理を続行させることができます(非同期実行)。 9 * 10 * このサンプルでは、5秒かかる処理をバックグラウンドで実行し、 11 * メインスクリプトは待たずに即座に終了します。 12 * 13 * @param string $command 実行したいコマンド文字列 14 * @return void 15 */ 16function executeCommandAsynchronously(string $command): void 17{ 18 // OSファミリーを判定 19 if (PHP_OS_FAMILY === 'Windows') { 20 // Windowsの場合: `start /B` を先頭につけ、出力をNULにリダイレクト 21 $asyncCommand = sprintf('start /B %s > NUL 2>&1', $command); 22 } else { 23 // LinuxやmacOSなどのUnix系OSの場合: 末尾に `&` をつけ、出力を/dev/nullにリダイレクト 24 $asyncCommand = sprintf('%s > /dev/null 2>&1 &', $command); 25 } 26 27 // exec関数で非同期コマンドを実行 28 // 第2、第3引数は非同期実行では通常使われません 29 exec($asyncCommand); 30} 31 32// --- 以下、実行サンプル --- 33 34// バックグラウンドで実行させたい時間のかかるコマンドの例 35// (5秒間スリープした後に、現在時刻をファイルに書き込むPHPコマンド) 36$longRunningTask = 'php -r "sleep(5); file_put_contents(\'result.txt\', \'Completed at \' . date(\'H:i:s\'));"'; 37 38echo '非同期処理を開始します。現在時刻: ' . date('H:i:s') . PHP_EOL; 39 40// 上記のコマンドを非同期で実行 41executeCommandAsynchronously($longRunningTask); 42 43echo 'メインスクリプトは待機せずに次の処理へ進みます。' . PHP_EOL; 44echo 'スクリプトの実行はここで終了します。現在時刻: ' . date('H:i:s') . PHP_EOL; 45echo '約5秒後に "result.txt" というファイルが作成されるか確認してください。' . PHP_EOL; 46
PHPのexec関数は、サーバー上で外部のコマンドを実行するための関数です。第一引数に実行したいコマンドの文字列を渡します。通常、exec関数は指定したコマンドの処理が完了するまでPHPスクリプトの実行を停止させます。これを同期実行と呼びます。
このサンプルコードは、時間のかかる処理を待たずに行う「非同期実行」の方法を示しています。これを実現するために、OSの特性を利用してコマンドの記述を工夫します。LinuxやmacOSのようなUnix系OSではコマンドの末尾に&を、Windowsでは先頭にstart /Bを付け加えます。これにより、PHPスクリプトはコマンドの終了を待たずに次の処理へ進むことができます。また、> /dev/null 2>&1という部分は、コマンドの出力結果を破棄するための記述で、これによってもスクリプトが待機しなくなります。
exec関数の第二引数$outputと第三引数$result_codeは、それぞれコマンドの出力と終了コードを受け取るための変数ですが、非同期実行の場合はコマンドの終了を待たないため、これらの情報を取得することはできません。戻り値は通常、コマンド出力の最終行を返しますが、このサンプルでは出力を破棄しているため、意味のある値は返りません。
このサンプルでは、5秒間スリープする処理を非同期で実行しています。そのため、メインスクリプトは5秒待つことなく即座に終了メッセージを表示します。その裏側でコマンドが実行され続け、約5秒後にファイルが作成されることで非同期処理の成功を確認できます。
exec関数にユーザー入力などの外部変数を渡す際は、escapeshellarg()関数で必ずエスケープしてください。これを怠ると、悪意のあるコマンドを実行される「コマンドインジェクション」という脆弱性の原因になります。また、この非同期実行の方法は、コマンドの実行結果や成功・失敗を知ることができません。サーバーの設定によってはexec関数自体が無効化されている場合もあります。バックグラウンドで多数のプロセスを起動するとサーバーに大きな負荷がかかるため、頻繁な実行は避けるべきです。より本格的な非同期処理には、専用のジョブキューシステムの利用を検討しましょう。