有没有一种方法可以在不等待命令完成的情况下使用shell_exec?

105

我有一个过程密集型的任务,希望能在后台运行。

用户点击页面,PHP脚本运行,最后根据某些条件,如果需要,则必须运行shell脚本,例如:

shell_exec('php measurePerformance.php 47 844 email@yahoo.com');

我目前使用shell_exec, 但是这需要脚本等待输出。有没有方法可以执行我想要的命令而不必等待其完成?


请立即开始该进程,因此 cron 不是一个选项。 - ToughPal
2
仅需简单提醒:请确保页面不能被所有人(搜索引擎/欺诈用户)访问,如果您在共享环境中,则所有脚本的执行时间(特别是PHP!)可能会受到限制。无论如何,您可能需要查看set_time_limit/php.ini。 - merkuro
可能是php执行后台进程的重复问题。 - Sean the Bean
8个回答

160

加上怎么样?

"> /dev/null 2>/dev/null &"

shell_exec('php measurePerformance.php 47 844 email@yahoo.com > /dev/null 2>/dev/null &');
请注意,这也会摆脱stdio和stderr。

2
没有副作用。如果您有时决定要获取进程的stdio或stderr输出,请考虑访问https://dev59.com/-XVD5IYBdhLWcg3wOo9h#45966 - jitter
1
问题:那只是丢弃输出,对吧?PHP在继续执行之前是否仍在等待进程完成,还是直接启动并忘记它? - Alana Storm
5
是的,丢弃掉。是的,点火并忘记。 - jitter
6
注意:在其他答案中看到的 > /dev/null 2>&1 &> /dev/null 2>/dev/null & 的简洁形式。这基本上是说“将标准错误(2)重定向到与标准输出(&1)相同的地方”。 - rymo
3
通常我会在执行命令的末尾添加“echo $! >> /tmp/pid.txt”来获取正在运行的脚本的 PID,但是如果将输出和错误重定向到/dev/null,则不再生成PID文件:有没有人可以在不等待输出的情况下获取由exec启动的脚本的PID? - hugsbrugs
显示剩余4条评论

29

这将执行一个命令并断开与正在运行的进程的连接。当然,它可以是任何你想要的命令。但为了测试,你可以创建一个带有 sleep(20) 命令的 PHP 文件。

exec("nohup /usr/bin/php -f sleep.php > /dev/null 2>&1 &");

这两者哪个更好,是使用这个还是shell_exec? - realtebo
你好,我能使用这个命令运行函数吗?shell_exec("nohup /usr/bin/php -f http://example.com/notifications/sendnotifications_async > /dev/null 2>&1 &"); - Waseem Bashir
1
为什么需要同时使用 nohup& - bugmenot123

15

你还可以立即将输出返回给客户端,并在此之后继续处理你的PHP代码。

这是我用于长时间等待Ajax调用的方法,对客户端没有任何影响:

ob_end_clean();
ignore_user_abort();
ob_start();
header("Connection: close");
echo json_encode($out);
header("Content-Length: " . ob_get_length());
ob_end_flush();
flush();
// execute your command here. client will not wait for response, it already has one above.

您可以在此处找到详细的解释:http://oytun.co/response-now-process-later


1
哎呀,我在这里点起了一个僵尸问题,我没有意识到。但是这些信息可能对其他人有益。 - Oytun
3
这与shell_exec 毫无关系。 - bugmenot123
我正在使用这个进行本地到远程的测试。在远程上,我使用shell_exec。谢谢 :) - bobble bubble
虽然这与问题无关,但这是一个重要的侧记...非常有用。 - AdheneManx

10
在Windows 2003上,要调用另一个脚本而不等待,我使用了以下代码:
$commandString = "start /b c:\\php\\php.EXE C:\\Inetpub\\wwwroot\\mysite.com\\phpforktest.php --passmsg=$testmsg"; 
pclose(popen($commandString, 'r'));

只有在更改cmd.exe的权限后才能正常工作 - 为IUSR_YOURMACHINE添加读取和执行权限(我还将写入权限设置为拒绝)。


9
使用PHP的popen命令,例如:
pclose(popen("start c:\wamp\bin\php.exe c:\wamp\www\script.php","r"));

这将创建一个子进程,脚本将在后台执行而不等待输出。

4
等待子进程结束。至少在Windows上是这样。 - Max Chernopolsky
这里的后台处理是通过Windows的“start”命令来实现的,它在要运行的exe前缀中起作用,如果你省略它,它将无法工作,我也不希望它能在另一个操作系统上工作...但是对我来说确实有效,谢谢! :-) - Antony
1
这个命令在Win 10上运行没有弹出CLI窗口:start /B php wait.php args - bobble bubble

9
当然,对于windows系统您可以使用以下方式:
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("C:/path/to/php-win.exe -f C:/path/to/script.php", 0, false);

注意:

如果出现COM错误,请在php.ini中添加扩展并重启apache

[COM_DOT_NET]
extension=php_com_dotnet.dll

我不知道,但这对我有用。请问有人能解释一下wscript.shell是什么以及它的作用吗? - GeekWithGlasses
请问为什么这行代码没有输出任何内容?$oExec = $WshShell->Run('C:\Users\Shreyash\Desktop\phantomjs-2.1.1-windows\bin\phantomjs.exe -f C:\xampp\htdocs\composer\test_0.js', 0, false); - GeekWithGlasses
你的 test_0.js 可能出现了错误,请检查幻影日志。 - Pedro Lobito

1
如果是从网页上获取数据,我建议生成某种信号(例如将文件放入目录中),然后使用cron作业来处理需要完成的工作。否则,我们可能会涉及在Apache进程内部使用pcntl_fork()exec(),这是不好的做法。

1
你说这是个坏魔法。能否详细说明一下? - Mayank

1

这样做是可行的,但您必须小心,不要过载服务器,因为每次调用此函数都会创建一个在后台运行的新进程。如果同时只有一个并发调用,则此解决方法将完成工作。

如果不是这样,我建议运行消息队列,例如beanstalkd/gearman/amazon sqs。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接