system — 外部コマンドを同期実行して標準出力をそのまま表示する

PHP
  • カテゴリ: PHP
  • 掲載バージョン: PHP 8.4
  • 名前空間 / FQCN / コマンド: {system()}
  • 関連: exec / shell_exec / passthru / proc_open / escapeshellarg / escapeshellcmd
  • 変更履歴: 主要仕様は近年変更なし(safe_mode は PHP 5.4 で廃止)

要点(TL;DR)

  • OSコマンドを実行し、出力を即時に標準出力へ流す。戻り値は最終行、終了コードは第2引数で受け取る。
  • 最低限の使い方:$last = system('ls -1 ' . escapeshellarg($dir), $status);
  • よくある罠:
    • コマンドインジェクション(必ず escapeshellarg / escapeshellcmd
    • Web実行で出力がレスポンスに混ざる
    • 無タイムアウトブロッキングdisable_functions で無効化され得る

概要

system() は外部コマンドを同期実行し、子プロセスの標準出力をそのまま PHP の標準出力に書き出します。戻り値で出力の最終行を、引数で終了コードを取得できます。CLI ツールでの進捗表示には便利ですが、Web 実行ではレスポンス汚染・ハングの原因になりがちです。

構文 / シグネチャ

system(
    string $command,
    int &$result_code = null
): string|false
  • 引数(表)
引数必須既定値説明
$commandstring実行するシェルコマンド。メタ文字含む値は必ずエスケープ
&$result_codeint(参照)nullプロセスの終了コード(0=成功が多い)
  • 戻り値string|false(最終行の文字列。失敗時は false
  • 例外/副作用:失敗時に E_WARNING。標準出力へ即時出力。プロセス生成・環境依存。disable_functions=system,exec,... で無効化される場合あり。

使用例

最小例(依存なし・そのまま動く)

<?php
declare(strict_types=1);

// PHP 自身を使って「hello」を出力するワンライナーを実行(OS非依存)
$cmd = escapeshellarg(PHP_BINARY) . ' -r "echo \"hello\";"';

ob_start();                                 // system は直接出力するためバッファ開始
$lastLine = system($cmd, $status);
$output = ob_get_clean();

printf("status=%d, lastLine=%s, captured=%s\n", $status, $lastLine, $output);
if ($status !== 0) {
    throw new RuntimeException("Command failed with status {$status}");
}

実務例(安全な引数組み立てと出力取得)

<?php
declare(strict_types=1);

function runListFiles(string $dir): array {
    // ユーザー入力を必ずエスケープ
    $cmd = 'ls -1 ' . escapeshellarg($dir);

    ob_start();
    $lastLine = system($cmd, $status);
    $all = ob_get_clean();

    if ($status !== 0) {
        throw new RuntimeException("ls failed (status={$status})");
    }

    // OSや環境によってはエンコーディング変換が必要(例: Windows→UTF-8)
    if (PHP_OS_FAMILY === 'Windows') {
        $all = mb_convert_encoding($all, 'UTF-8', 'CP932');
    }

    return [
        'lines'    => array_values(array_filter(explode("\n", rtrim($all)))),
        'lastLine' => $lastLine,
        'status'   => $status,
    ];
}

$result = runListFiles(__DIR__);
print_r($result);

よくある落とし穴・注意

  • インジェクション:ユーザー入力をそのまま連結しない。引数は escapeshellarg()、コマンドテンプレートは固定。
  • 出力が直接混ざる:Web ルートで使うと HTML に混入。ob_start() で捕捉するか、初めから exec() / shell_exec() を選ぶ。
  • タイムアウト無し:長時間ブロック。必要なら proc_open() + 非同期読取や外部ライブラリ(Symfony Process)でタイムアウト制御。
  • 環境依存PATH、利用コマンドの有無、OS差(ls/dir)に注意。
  • 無効化される場合php.inidisable_functions で拒否されることがある。
  • エンコーディング:Windows は出力が CP932 のことが多い。mb_convert_encoding で UTF-8 へ。

代替・関連APIとの比較

  • exec():出力を返り値や配列で取得。標準出力にそのままは書かれない。ログ収集向き。
  • `cmd`(バッククォート)shell_exec() と同等。全出力を文字列で取得。最終行だけ不要ならこちらが簡単。
  • passthru()バイナリ出力をそのまま送出(画像/zip 等)。戻り値はなし、終了コードは第2引数で。
  • proc_open():入出力ストリームを細かく制御(非同期・タイムアウト・環境変数・カレントディレクトリ)。本格運用向け。
  • Symfony Process:高機能なプロセス制御の安全なラッパー。再試行・タイムアウト・逐次出力取得が容易。

選定基準

  • 画面に逐次表示したい → system() / passthru()
  • すべての出力をまとめて受け取りたい → shell_exec() / バッククォート
  • 堅牢性(タイムアウト/並列/環境)重視proc_open() / Symfony Process

テスト例(Pest)

<?php
// tests/SystemFunctionTest.php
it('runs a one-liner with system and captures output', function () {
    $cmd = escapeshellarg(PHP_BINARY) . ' -r "echo \"123\";"';

    ob_start();
    $last = system($cmd, $status);
    $all  = ob_get_clean();

    expect($status)->toBe(0);
    expect($last)->toBe('123');
    expect(trim($all))->toBe('123');
});

トラブルシュート(エラー別)

症状/エラー原因対処
system() has been disabled for security reasonsdisable_functions で無効化CLI 専用 PHP を使う/サーバ設定変更を依頼/proc_open() 等へ切替
sh: 1: <cmd>: not found / 'cmd' is not recognizedコマンド未インストール / PATH 未設定絶対パス指定、PATH 追加、パッケージ導入
出力がHTMLに混ざるsystem()が直接出力ob_start()で捕捉、exec()系へ変更
ハング・応答なし長時間処理・待ち状態proc_open()/Symfony Process でタイムアウト設定、ジョブキュー化
文字化けOS毎の文字コード差mb_convert_encoding($out, 'UTF-8', 'CP932') 等で変換
終了コードが非0コマンド失敗標準エラー確認(2>&1 で統合 or proc_open() で分離)、引数・権限を再確認

参考リンク

レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする