我有一个 PHP 脚本,它需要调用一个 shell 脚本,但根本不关心输出。 shell 脚本进行了许多 SOAP 调用并且完成速度很慢,所以我不想在 PHP 请求等待回复时减慢它的速度。事实上,PHP 请求应该能够在不终止 shell 进程的情况下退出。
我研究了各种 exec()
、shell_exec()
、pcntl_fork()
等函数,但它们似乎都没有提供我想要的东西。 (或者,如果他们这样做,我不清楚怎么做。)有什么建议吗?
nice
和 ionice
来防止 shell 脚本使您的系统不堪重负(例如 /usr/bin/ionice -c3 /usr/bin/nice -n19
)
如果它“不关心输出”,难道不能用 &
调用脚本的 exec 来后台进程吗?
编辑 - 结合 @AdamTheHut 对此帖子的评论,您可以将其添加到对 exec
的调用中:
" > /dev/null 2>/dev/null &"
这会将 stdio
(第一个 >
)和 stderr
(2>
)重定向到 /dev/null
并在后台运行。
还有其他方法可以做同样的事情,但这是最简单的阅读方式。
上述双重重定向的替代方案:
" &> /dev/null &"
我为此使用了 at ,因为它确实开始了一个独立的过程。
<?php
`echo "the command"|at now`;
?>
www-data
)没有使用 at
的权限并且您无法对其进行配置,您可以尝试使用 <?php exec('sudo sh -c "echo \"command\" | at now" ');
如果 command
包含引号,请参阅 {1 } 省去你的头疼
echo "sudo command" | at now
并在 /etc/at.deny
中注释掉 www-data
at
包(和命令)对于某些 linux 发行版来说是非默认的。它还需要运行 atd
服务。由于缺乏解释,该解决方案可能难以理解。
致所有 Windows 用户:我找到了一种运行异步 PHP 脚本的好方法(实际上它几乎适用于所有东西)。
它基于 popen() 和 pclose() 命令。并且在 Windows 和 Unix 上运行良好。
function execInBackground($cmd) {
if (substr(php_uname(), 0, 7) == "Windows"){
pclose(popen("start /B ". $cmd, "r"));
}
else {
exec($cmd . " > /dev/null &");
}
}
原始代码来自:http://php.net/manual/en/function.exec.php#86329
myscript.js
,而是输入 node myscript.js
。也就是说:node 是可执行文件,myscript.js 是要执行的脚本。可执行文件和脚本之间存在巨大差异。
php artisan
只需在此处添加评论,因此无需跟踪为什么它不能与命令一起使用
在 linux 上,您可以执行以下操作:
$cmd = 'nohup nice -n 10 php -f php/file.php > log/file.log & printf "%u" $!';
$pid = shell_exec($cmd);
这将在命令提示符处执行命令,然后只返回 PID,您可以检查它是否 > 0 以确保它有效。
这个问题类似:Does PHP have threading?
action=generate var1_id=23 var2_id=35 gen_id=535
部分),则此答案将更易于阅读。此外,由于 OP 询问有关运行 shell 脚本的问题,因此您不需要特定于 PHP 的部分。最终代码为:$cmd = 'nohup nice -n 10 /path/to/script.sh > /path/to/log/file.log & printf "%u" $!';
nice
,而且还使用 ionice
。
&
在后台运行前面的代码,然后 printf
用于包含 PID 的 $!
变量的格式化输出
在 Linux 中,您可以通过在命令末尾附加一个 & 符号来在一个新的独立线程中启动一个进程
mycommand -someparam somevalue &
在 Windows 中,您可以使用“开始”DOS 命令
start mycommand -someparam somevalue
start
命令,它不会异步运行...您能否包括您从中获得该信息的来源?
/B
参数。我在这里解释过:stackoverflow.com/a/34612967/1709903
正确的方法(!)做到这一点是
fork() setid() execve()
fork 分叉,setsid 告诉当前进程成为主进程(没有父进程),execve 告诉调用进程由被调用进程替换。这样父母就可以在不影响孩子的情况下退出。
$pid=pcntl_fork();
if($pid==0)
{
posix_setsid();
pcntl_exec($cmd,$args,$_ENV);
// child becomes the standalone detached process
}
// parent's stuff
exit();
我用这个...
/**
* Asynchronously execute/include a PHP file. Does not record the output of the file anywhere.
* Relies on the PHP_PATH config constant.
*
* @param string $filename file to execute
* @param string $options (optional) arguments to pass to file via the command line
*/
function asyncInclude($filename, $options = '') {
exec(PHP_PATH . " -f {$filename} {$options} >> /dev/null &");
}
(其中 PHP_PATH
是一个类似 define('PHP_PATH', '/opt/bin/php5')
或类似定义的常量)
它通过命令行传入参数。要在 PHP 中阅读它们,请参阅 argv。
我还发现 Symfony Process Component 对此很有用。
use Symfony\Component\Process\Process;
$process = new Process('ls -lsa');
// ... run process in background
$process->start();
// ... do other things
// ... if you need to wait
$process->wait();
// ... do things after the process has finished
在它的 GitHub repo 中查看它是如何工作的。
proc_*
内部函数。
我发现真正对我有用的唯一方法是:
shell_exec('./myscript.php | at now & disown')
shell_exec("/usr/local/sbin/command.sh 2>&1 >/dev/null | at now & disown")
,得到的只是:sh: 1: disown: not found
您还可以将 PHP 脚本作为 daemon 或 cronjob 运行:#!/usr/bin/php -q
使用命名的 fifo。
#!/bin/sh
mkfifo trigger
while true; do
read < trigger
long_running_task
done
然后,每当您想启动长时间运行的任务时,只需编写一个换行符(非阻塞到触发器文件。
只要您的输入小于 PIPE_BUF
并且它是单个 write()
操作,您就可以将参数写入 fifo 并让它们在脚本中显示为 $REPLY
。
不使用队列,您可以像这样使用 proc_open()
:
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w") //here curaengine log all the info into stderror
);
$command = 'ping stackoverflow.com';
$process = proc_open($command, $descriptorspec, $pipes);
不定期副业成功案例分享
&> /dev/null &
,如果您使用它,xdebug 将不会生成日志。检查stackoverflow.com/questions/4883171/…/dev/null
是更好的做法,因为写入已关闭的 FD 会导致错误,而尝试读取或写入/dev/null
只是默默地什么都不做。