我试图从 php 脚本中执行 perl 脚本。我已经使用各种方法(例如 exec、popen 和 proc_open)完成了这项工作,但我有几个问题需要解决,而老好的 Google 并没有给我答案。
我需要从 php 文件中运行 .pl 脚本(将一个变量传递给脚本,该变量是一个数字),但停止 php 脚本等待,直到 .pl 完成(.pl 可能需要 4-5 小时)在服务器上运行)。我不期望 perl 脚本有任何返回输出(perl 脚本将其输出记录到 mysql 数据库),所以我只需要它在服务器上运行并让 php 脚本继续运行。
由于它在运行 apache、php、mysql 的 Windows 机器上运行,因此需要克服一些障碍。
我想我已经看到了 Linux 的解决方案,但它需要保留在 Windows 机器上。
我当前正在尝试使用以下代码的 proc_open 方法(proc_open 行上的 35 是我需要传递给 perl 脚本的测试 ID):
$descriptorspec = array(
0 => array("pipe","r"),
1 => array("pipe","w"),
2 => array("file","./error.log","a")
) ;
proc_close(proc_open('perl perlscript.pl 35', $descriptorspec, $pipes));
$i = 0;
while ($i < 1000) {
echo ++$i;
}
现在此代码确实执行 perl 脚本,但我在其后面放置了 while 循环(只是为了测试)永远不会执行(我没有等待 perl 脚本完成来看看它是否执行),因为它必须等待 .pl 完成。
Im trying to execute a perl script from within a php script. I have had this working using various methods such as exec, popen and proc_open but I have a couple of issues to get around which the good old Google isnt giving me the answers to.
I need to run the .pl script (passing one variable to the script which is a number) from within the php file but stop the php script from waiting until the .pl has finished (the .pl is likely to take 4-5 hours to run on the server). Im not expecting any return output from the perl script (the perl script logs its output to a mysql db) so I just need it to be running on the server and let the php script carry on.
There are some barriers to get past as its running on a Windows machines running apache, php, mysql.
I think Ive seen the solution for linux but it needs to stay on the Windows machine.
Im current trying a proc_open approach using the following code (the 35 on the proc_open line is a test id I need to pass to the perl script):
$descriptorspec = array(
0 => array("pipe","r"),
1 => array("pipe","w"),
2 => array("file","./error.log","a")
) ;
proc_close(proc_open('perl perlscript.pl 35', $descriptorspec, $pipes));
$i = 0;
while ($i < 1000) {
echo ++$i;
}
Now this code does execute the perl script but the while loop Ive placed after it (just for testing) never executes (ive not waited for the perl script to finish to see if it does) as it must be waiting for the .pl to finish.
发布评论
评论(4)
我最近发布了一个用于执行此类任务的模块: Win32::Detached
将其添加到您的 perl 脚本中并它会自行守护进程,让您的 PHP 脚本继续运行。
I recently published a module for tasks like this: Win32::Detached
Add it to your perl script and it will daemonize itself, allowing your PHP script to move on.
我可以看到 3 个选项:
在 Perl 脚本中使用
fork
来分叉实际工作,并在 fork 之后立即存在父进程。不过,这在 Windows 上可能无法正常工作或根本无法工作,但值得一试。请参阅以下“Windows 上的
fork
”参考:如果 PHP 可以发起 URL 请求(类似于 Perl 的 LWP 或wget),然后将 Perl 脚本转换为 CGI 脚本(简单),放入 Apache 的 CGI 目录,并通过正确的 URL 调用它:
http://localhost/...
。然后,在最坏的情况下,通过某种内部 PHP 超时或常规 HTTP 超时来使 URL 请求超时。在最坏的情况下,在 Perl 代码中执行相同的技巧,因为 Perl 绝对可以支持通过 LWP 库的 URL 调用以及通过alarm()
让调度程序启动您的 Perl 脚本每 X 分钟。该脚本应以以下逻辑开始:
if (-e $flag_file) { unlink $flag_file; do_long_work() } else { 退出 0; }
其中$flag_file
是服务器上的特殊位置(例如C:\temp\myscript_start_perl.txt
),do_long_work()
code> 包含需要完成的实际工作。然后,当 PHP 脚本需要启动 Perl 脚本时,它只是创建一个空的标志文件(相同的
C:\temp\myscript_start_perl.txt
),Perl 会注意到该文件调度程序下次执行它时编写脚本。I can see 3 options:
Use
fork
in your perl script to fork off the actual work with the parent process existing right after fork. This may not work correctly or at all on Windows, however, but worth a try.Please see the following "
fork
on Windows" references:If PHP can launch a URL request (similar to Perl's LWP or wget), then convert your Perl script into a CGI script (trivial), stick into your Apache's CGI directory, and call it via a proper URL to
http://localhost/...
. Then time out the URL request via either some iternal PHP timeout or regular HTTP timeout in worst case. In REALLY worst case, do the same trick from within Perl code since Perl can definitely support both the URL call via LWP libraries as well as timeout viaalarm()
Have a scheduler launch your Perl script every X minutes. The script should start with the following logic:
if (-e $flag_file) { unlink $flag_file; do_long_work() } else { exit 0; }
where$flag_file
is the special location on the server (sayC:\temp\myscript_start_perl.txt
), anddo_long_work()
contains the actual work needing to be done.Then, when PHP script needs to kick off the Perl script, it simply creates an empty flag file (the same
C:\temp\myscript_start_perl.txt
), which will be noticed by the Perl script next time the scheduler executes it.有两种可能性
使用“启动”程序。您将无法使用管道,只能使用命令行参数。
创建一个始终运行的 Perl 程序(在启动时运行或使用 Win32::Daemon)。使用套接字或 http 连接到它。这很容易,因为 CPAN 上有大量现成的服务器
。此外,也可以使用 Win32::Job 或 Win32::Process 或其他一些 CPAN 模块从另一个 Perl 程序来完成此操作。
There are two possibilities
Use "start" program. You will not be able to use pipes, only command line arguments.
Create an always-running Perl program (run at startup or using Win32::Daemon). Connect to it using sockets or http. It is easy, as there are plenty of ready servers on CPAN
Also, It may be possible to do this from another Perl program using Win32::Job or Win32::Process or some other CPAN module.
感谢所有回答这个问题的人。经过大量研究和尝试不同的解决方案后,我终于找到了一些可以工作并满足我们需求的东西。它远非完美,但嘿,它有效,所以我不会抱怨太多。
因为这似乎是其他人想知道的事情,所以我想我会分享我的解决方案。
我从这里下载了一个名为 Hstart (40kb) 的小程序:http://www .ntwind.com/software/utilities/hstart.html
我将文件提取到 c:\windows\system32。
hstart 程序能够在不打开命令窗口的情况下运行批处理文件。它无法直接调用 .pl 文件,因此我必须创建一个 .bat 来调用 .pl,如下所示。
创建了一个与我的 .pl 文件相同目录的 .bat 文件,该文件基本上使用本地安装的 perl 并传递变量来调用 perl 脚本。如这里所示:
perl script.pl %1
在我的 PHP 脚本中,我使用以下命令来调用我的 perl 文件。
$cmd = 'hstart /NOCONSOLE "run.bat ' . $id . '" >日志.日志';
exec($cmd);
由于设置了标志,运行 php 文件会导致 hstart 以 noconsole 打开,使用我作为变量传递的 $id 打开 perl 文件,并将隐藏控制台窗口的所有输出发送到 log.log(按照我的命令指定) 。我只能看到 perl.exe 在我的任务管理器中运行。
正如我所说,远非完美,但它确实有效。
Thanks to everyone who answered this question. After much research and messing with different solutions I finally got something to work and suit our needs. Its far from perfect but hey it works so Im not going to complain too much.
Since this seems to be something other people want to know Id thought Id share my solution.
I downloaded a tiny program called Hstart (40kb) from here: http://www.ntwind.com/software/utilities/hstart.html
I extracted the files to c:\windows\system32.
The hstart program has the ability to run batch files without opening a command window. It wont work calling the .pl file directly so I had to create a .bat to call the .pl as follows.
created a .bat file same directory as my .pl file which basically called the perl script using locally installed perl and passing a variable. As show here:
perl script.pl %1
In my PHP script I used the following command to call my perl file.
$cmd = 'hstart /NOCONSOLE "run.bat ' . $id . '" > log.log';
exec($cmd);
Running the php file causes hstart to open with noconsole due to the flag set, open the perl file with the $id I passed as a variable and send all output of the hidden console window to log.log as specified with my command. All I can see is perl.exe running in my task manager.
As I said far from perfect but it works.