Exec: примеры (PHP)

Функция exec в PHP: полный обзор с практическими примерами
Раздел: Системные команды
exec(string $command, array &$output = null, int &$result_code = null): string|false

Описание функции exec

Функция exec() выполняет внешнюю программу, переданную ей в качестве аргумента. Она является частью семейства функций PHP для выполнения команд операционной системы и часто используется для взаимодействия с сервером.

Назначение и сценарии использования

Функция находит применение в задачах системного администрирования через веб-интерфейс, обработки файлов с помощью внешних утилит, запуска скриптов других языков или длительных фоновых процессов.

Аргументы функции

Сигнатура функции: exec(string $command, array &$output = null, int &$result_code = null): string|false

  • $command (string): Команда для выполнения.
  • $output (array &): Если передан массив, он будет заполнен каждой строкой вывода команды. Конечные пробелы в строках удаляются.
  • $result_code (int &): Если присутствует этот аргумент, в него будет записан код возврата выполненной команды.

Функция возвращает последнюю строку вывода команды или false в случае ошибки.

Краткие примеры использования

Простой запуск команды

Выполнение команды и получение последней строки вывода.

$last_line = exec('ls -la', $output, $return_code);
echo "Последняя строка: $last_line\n";
echo "Код возврата: $return_code";
Последняя строка: ...
Код возврата: 0
Получение всего вывода
exec('whoami', $output_full);
print_r($output_full);
Array
(
    [0] => www-data
)
Использование кода возврата
exec("cp несуществующий_файл новое_место 2>&1", $out, $code);
echo "Команда завершилась с кодом: $code";
print_r($out);
Команда завершилась с кодом: 1
Array
(
    [0] => cp: cannot stat 'несуществующий_файл': No such file or directory
)

Альтернативные функции в PHP

Возвращает весь вывод команды в виде строки. Удобна, когда нужен полный вывод без разбиения на строки.

system

Выполняет команду, выводит результат непосредственно в браузер (или stdout) и возвращает последнюю строку. Полезна для быстрой отладки.

Выполняет команду и передает сырой вывод напрямую в браузер (или stdout). Часто используется для двоичных данных, например, изображений.

proc_open

Обеспечивает максимальный контроль над выполнением процесса: позволяет работать с stdin, stdout и stderr отдельно. Предпочтительна для сложных взаимодействий.

Функцию exec обычно выбирают, когда нужен массив строк вывода и код возврата.

Аналоги в других языках программирования

Python (subprocess.run)
import subprocess
result = subprocess.run(['ls', '-la'], capture_output=True, text=True)
print(result.stdout)
print(f"Код возврата: {result.returncode}")
... вывод команды ...
Код возврата: 0
JavaScript (Node.js, child_process.exec)
const { exec } = require('child_process');
exec('ls -la', (error, stdout, stderr) => {
  console.log(stdout);
  if (error) console.log(`Код ошибки: ${error.code}`);
});

Exec в MySQL

В MySQL прямой аналог отсутствует. Близкая по духу функция SYSTEM() (доступна с MySQL 8.0.19) выполняет команду оболочки, но возвращает только код выхода, а не её вывод.

SYSTEM ls -la; -- Возвращает только код выхода

Типичные ошибки

Неэкранированные пользовательские данные
// ОПАСНО
$user_input = $_GET['filename'];
exec("rm /tmp/$user_input"); // Пользователь может ввести "..\/..\/etc\/passwd"

Решение: использование escapeshellarg().

$user_input = $_GET['filename'];
exec("rm /tmp/" . escapeshellarg($user_input));
Игнорирование кода возврата и stderr
exec("some_failing_command", $output);
echo "Всё ок"; // Команда могла завершиться с ошибкой
Всё ок

Решение: всегда проверять код возврата и перенаправлять stderr в stdout.

exec("some_failing_command 2>&1", $output, $code);
if ($code !== 0) {
    echo "Ошибка: ";
    print_r($output);
}

Изменения в последних версиях PHP

В PHP 8.0 значительных изменений в поведении функции exec не было. Однако в PHP 7.4 и более ранних версиях стоит отметить:

  • Ведется постоянная работа над безопасностью функций, выполняющих команды оболочки.
  • Рекомендации по использованию escapeshellarg() и escapeshellcmd() остаются критически важными.
  • В целом, поведение функции остается стабильным на протяжении многих выпусков.

Расширенные примеры

Асинхронный запуск и сбор вывода
Пример php
// Запуск длительной команды в фоне и запись вывода в файл
$command = 'nohup ./long_script.sh > /tmp/output.log 2>&1 & echo $!';
$pid = exec($command);
echo "Запущен процесс с PID: $pid";
Параллельное выполнение нескольких команд
Пример php
$commands = [
    "sleep 2 && echo 'Первый'",
    "sleep 1 && echo 'Второй'",
];
$pids = [];
foreach ($commands as $cmd) {
    $pid = exec("nohup bash -c '" . escapeshellcmd($cmd) . "' > /dev/null 2>&1 & echo $!");
    $pids[] = $pid;
}
echo "Запущены процессы: " . implode(', ', $pids);
Взаимодействие через stdin

Использование proc_open предпочтительнее, но с exec можно эмулировать через echo.

Пример php
$input_data = "Строка 1\nСтрока 2";
$command = "cat";
exec("echo " . escapeshellarg($input_data) . " | " . $command, $output);
print_r($output);
Array
(
    [0] => Строка 1
    [1] => Строка 2
)
Ограничение времени выполнения команды
Пример php
// Использование timeout
$result = exec("timeout 5s slow_command.sh 2>&1", $output, $code);
if ($code == 124) {
    echo "Команда была остановлена по таймауту.";
}
Безопасное построение команды с аргументами
Пример php
$args = ['-l', '-a', 'каталог с пробелами'];
$safe_args = array_map('escapeshellarg', $args);
$command = "ls " . implode(' ', $safe_args);
// ls '-l' '-a' 'каталог с пробелами'
exec($command, $output);

PHP exec function comments

En
Exec Execute an external program