PHP 即使在 Windows 后台执行 运行 命令后,脚本仍卡在 exec 上
PHP script stuck on exec even after running command in backgroud on Windows
我想要一个 php 脚本,我可以从中执行程序,如果它没有在 2 秒内完成执行则终止它。我正在使用 Windows。我试过以下代码:
exec("start /B program.exe");
sleep(2);
exec('taskkill /F /IM "program.exe"');
这似乎不起作用,因为只要 program.exe
未完成执行,脚本就会卡在第一个 exec
语句上。我不知道如何解决这个问题。
对,exec()
会阻塞直到执行完成。 This question has great answers for how to do an exec()
with a timeout. I think this 可能最适合您。为了完整起见,我将 post 此处的代码(但我不能接受任何信用!):
/**
* 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;
}
编辑
proc_open()
的 'exec' 部分可能不适用于 Windows,但可能没有必要。
exec()
手册页的 first comment 显示了一个非常简单的示例。
你是用 php cli (command line)? Open a command prompt as administrator 来做的吗?
为了不被等待程序close the process of opening the program.
阻塞
php myscript.php
pclose(popen("start /B program.exe", "r"));
sleep(2);
exec('taskkill /F /IM program.exe');
exit(0);
也可以将 exec start 放入单独的脚本中并使用 exec
启动该脚本
我想要一个 php 脚本,我可以从中执行程序,如果它没有在 2 秒内完成执行则终止它。我正在使用 Windows。我试过以下代码:
exec("start /B program.exe");
sleep(2);
exec('taskkill /F /IM "program.exe"');
这似乎不起作用,因为只要 program.exe
未完成执行,脚本就会卡在第一个 exec
语句上。我不知道如何解决这个问题。
对,exec()
会阻塞直到执行完成。 This question has great answers for how to do an exec()
with a timeout. I think this 可能最适合您。为了完整起见,我将 post 此处的代码(但我不能接受任何信用!):
/**
* 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;
}
编辑
proc_open()
的 'exec' 部分可能不适用于 Windows,但可能没有必要。
exec()
手册页的 first comment 显示了一个非常简单的示例。
你是用 php cli (command line)? Open a command prompt as administrator 来做的吗?
为了不被等待程序close the process of opening the program.
php myscript.php
pclose(popen("start /B program.exe", "r"));
sleep(2);
exec('taskkill /F /IM program.exe');
exit(0);
也可以将 exec start 放入单独的脚本中并使用 exec