exec () с таймаутом

Я ищу способ запустить процесс PHP с тайм-аутом. В настоящее время я просто использую exec() , но он не предоставляет параметр таймаута.

Я также попытался открыть процесс с помощью proc_open() и использовать stream_set_timeout() в результирующем канале, но это тоже не сработало.

Итак, есть ли способ запустить команду (точнее, PHP-команду) с тайм-аутом? (PS: Это относится к случаям, когда предел max_execution_time не работает, поэтому нет необходимости предлагать это.)

(Кстати, мне также нужно получить код возврата процесса.)

Я немного искал эту тему и пришел к выводу, что в некоторых случаях (если вы используете Linux) вы можете использовать команду «timeout». Это довольно гибкий

 Usage: timeout [OPTION] DURATION COMMAND [ARG]... or: timeout [OPTION] 

в моем конкретном случае я пытаюсь запустить sphinx indexer из PHP, своего рода сценарий данных миграции, поэтому мне нужно переиндексировать мои документы сфинкса

 exec("timeout {$time} indexer --rotate --all", $output); 

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

Я нашел это на php.net, который, я думаю, может делать то, что вы хотите

 <?php function PsExecute($command, $timeout = 60, $sleep = 2) { // First, execute the process, get the process ID $pid = PsExec($command); if( $pid === false ) return false; $cur = 0; // Second, loop for $timeout seconds checking if process is running while( $cur < $timeout ) { sleep($sleep); $cur += $sleep; // If process is no longer running, return true; echo "\n ---- $cur ------ \n"; if( !PsExists($pid) ) return true; // Process must have exited, success! } // If process is still running after timeout, kill the process and return false PsKill($pid); return false; } function PsExec($commandJob) { $command = $commandJob.' > /dev/null 2>&1 & echo $!'; exec($command ,$op); $pid = (int)$op[0]; if($pid!="") return $pid; return false; } function PsExists($pid) { exec("ps ax | grep $pid 2>&1", $output); while( list(,$row) = each($output) ) { $row_array = explode(" ", $row); $check_pid = $row_array[0]; if($pid == $check_pid) { return true; } } return false; } function PsKill($pid) { exec("kill -9 $pid", $output); } ?> 

Вы можете использовать fork() а затем exec() в одном процессе и wait() неблокировать в другом. Также отслеживайте таймаут и kill() другой процесс, если он не успевает закончить.

(Отказ от ответственности: я был удивлен тем, что не нашел для этого хорошего решения, затем просмотрел документацию по proc и нашел это довольно прямолинейно. Итак, вот простой ответ proc, который использует собственные функции таким образом, чтобы обеспечить согласованные результаты. по-прежнему ловить выходные данные для ведения журнала.)

Линия proc имеет функции proc_terminate ( process-handler ) , которые в сочетании с proc_get_status ( process-handler ) получают « proc_get_status ( process-handler ) » ключ, вы можете в то время как цикл со сном выполнять синхронный вызов exec с таймаутом.

Итак, в основном:

 $ps = popen('cmd'); $timeout = 5; //5 seconds $starttime = time(); while(time() < $starttime + $timeout) //until the current time is greater than our start time, plus the timeout { $status = proc_get_status($ps); if($status['running']) sleep(1); else return true; //command completed :) } proc_terminate($ps); return false; //command timed out :( 

timeout {$time} command решение timeout {$time} command не работает должным образом, когда оно вызывается из PHP-скрипта. В моем случае с командой ssh ​​на неправильный сервер (ключ rsa не найден, а сервер запрашивает пароль), процесс все еще сохраняется после определенного таймаута.

Однако я нашел функцию, которая отлично работает здесь:

http://blog.dubbelboer.com/2012/08/24/execute-with-timeout.html

С & Р:

 /** * Execute a command and return it's output. Either wait until the command exits or the timeout has expired. * * @param string $cmd Command to execute. * @param number $timeout Timeout in seconds. * @return string Output of the command. * @throws \Exception */ function exec_timeout($cmd, $timeout) { // File descriptors passed to the process. $descriptors = array( 0 => array('pipe', 'r'), // stdin 1 => array('pipe', 'w'), // stdout 2 => array('pipe', 'w') // stderr ); // Start the process. $process = proc_open('exec ' . $cmd, $descriptors, $pipes); if (!is_resource($process)) { throw new \Exception('Could not execute process'); } // Set the stdout stream to none-blocking. stream_set_blocking($pipes[1], 0); // Turn the timeout into microseconds. $timeout = $timeout * 1000000; // Output buffer. $buffer = ''; // While we have time to wait. while ($timeout > 0) { $start = microtime(true); // Wait until we have output or the timer expired. $read = array($pipes[1]); $other = array(); stream_select($read, $other, $other, 0, $timeout); // Get the status of the process. // Do this before we read from the stream, // this way we can't lose the last bit of output if the process dies between these functions. $status = proc_get_status($process); // Read the contents from the buffer. // This function will always return immediately as the stream is none-blocking. $buffer .= stream_get_contents($pipes[1]); if (!$status['running']) { // Break from this loop if the process exited before the timeout. break; } // Subtract the number of microseconds that we waited. $timeout -= (microtime(true) - $start) * 1000000; } // Check if there were any errors. $errors = stream_get_contents($pipes[2]); if (!empty($errors)) { throw new \Exception($errors); } // Kill the process in case the timeout expired and it's still running. // If the process already exited this won't do anything. proc_terminate($process, 9); // Close all streams. fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); return $buffer; }