shell_exec () управление тайм-аутом и exec () - PullRequest
7 голосов
/ 04 августа 2010

Я запускаю сторонний скрипт, используя написанный мной класс-оболочку, который вызывает shell_exec() и передает в файл, который я анализирую позже, используя код php.Я должен упомянуть, что это работает, но я пытаюсь улучшить функциональность, столкнувшись с вариантом использования, о котором я не задумывался.

Как лучше управлять таймаутом в shell_exec ()?Я думал об этом в try() catch(), но я не уверен, как лучше всего обработать компонент времени.

Я читал здесь несколько вопросов, касающихся shell_exec() и exec()и кажется, что, передав выходные параметры в exec(), вы можете получить возврат, но это зависит от того, завершится ли сценарий статусом возврата.Кроме того, на моей мини-тестовой странице я не могу заставить ее возвращать какой-либо вывод!

Другой вариант, о котором я подумал, - это использовать модальное диалоговое окно со спиннером в стиле ajax во время работы скрипта иустановка ручного тайм-аута в JavaScript.Который затем дал пользователю диалоговое сообщение с моделью о его сбое / тайм-ауте и завершении.

Существуют ли какие-либо приемлемые методы для этого варианта использования?

Мой мини-тест, состоящий из следующего,

public $e_return = array();
public $e_status = '';
// Paths are absolute from /
public function execCheck($domain){
    exec($this->ssl_check_path." -s ".$domain." -p 443 > ".$this->folder.$this->filename." 2>&1 &", &$this->e_return, &$this->e_status);
}

// Returns
Array
(
)

0

Используя этот вопрос в качестве ссылки, Невозможно выполнить скрипт PHP с использованием PHP exec

http://www.php.net/manual/en/function.exec.php

Ответы [ 3 ]

16 голосов
/ 08 ноября 2012

Я пишу какой-то рабочий кусок кода для такой задачи.Функция возвращает код выхода (0 - ОК,> 0 - ошибка) и записывает stdout, stderr в ссылочные переменные.

/*execute program and write all output to $out
terminate program if it runs more than 30 seconds */
execute("program --option", null, $out, $out, 30);
echo $out;

function execute($cmd, $stdin=null, &$stdout, &$stderr, $timeout=false)
{
    $pipes = array();
    $process = proc_open(
        $cmd,
        array(array('pipe','r'),array('pipe','w'),array('pipe','w')),
        $pipes
    );
    $start = time();
    $stdout = '';
    $stderr = '';

    if(is_resource($process))
    {
        stream_set_blocking($pipes[0], 0);
        stream_set_blocking($pipes[1], 0);
        stream_set_blocking($pipes[2], 0);
        fwrite($pipes[0], $stdin);
        fclose($pipes[0]);
    }

    while(is_resource($process))
    {
        //echo ".";
        $stdout .= stream_get_contents($pipes[1]);
        $stderr .= stream_get_contents($pipes[2]);

        if($timeout !== false && time() - $start > $timeout)
        {
            proc_terminate($process, 9);
            return 1;
        }

        $status = proc_get_status($process);
        if(!$status['running'])
        {
            fclose($pipes[1]);
            fclose($pipes[2]);
            proc_close($process);
            return $status['exitcode'];
        }

        usleep(100000);
    }

    return 1;
}
7 голосов
/ 04 августа 2010

Я бы посоветовал вам использовать proc_open.Вы можете настроить его так, чтобы он возвращал потоковый ресурс, сохранял таймер вручную, и, если время таймера истекает до завершения процесса, вы можете завершить его с помощью proc_terminate.Если он завершится до истечения таймера, вы можете использовать proc_close, а затем stream_get_contents, чтобы получить данные, которые в противном случае были бы записаны на стандартный вывод.

0 голосов
/ 27 октября 2016

Я пытался с popen(), но нет способа прекратить процесс после этого.Кроме того, stream_get_contents() блокирует поток даже при использовании stream_set_blocking в Windows, поэтому вместо этого мне пришлось использовать fread.Кроме того, proc_terminate не работает должным образом в Windows, поэтому мне пришлось использовать альтернативную функцию уничтожения.

Я придумал это, теперь она должна работать как в Windows, так и в Linux:

function execute($command, $timeout = 5) {
    $handle = proc_open($command, [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], $pipe);

    $startTime = microtime(true);

    /* Read the command output and kill it if the proccess surpassed the timeout */
    while(!feof($pipe[1])) {
        $read .= fread($pipe[1], 8192);
        if($startTime + $timeout < microtime(true)) break;
    }

    kill(proc_get_status($handle)['pid']);
    proc_close($handle);

    return $read;
}

/* The proc_terminate() function doesn't end proccess properly on Windows */
function kill($pid) {
    return strstr(PHP_OS, 'WIN') ? exec("taskkill /F /T /PID $pid") : exec("kill -9 $pid");
}
...