proc_open:扩展文件描述符编号以启用从Perl脚本获取“状态”反馈。

7
PHP的proc_open手册说明如下:

文件描述符号不仅限于0、1和2 - 您可以指定任何有效的文件描述符号,它将传递给子进程。这使您的脚本能够与其他作为“协处理器”运行的脚本进行交互。特别是,这对于以更安全的方式向PGP、GPG和openssl之类的程序传递口令以及从这些程序提供的辅助文件描述符上读取状态信息非常有用。

发生了什么:我在基于PHP的Web应用程序中调用一个Perl脚本并在调用中传递参数。我没有未来需要向脚本发送数据的需求。通过stdout [1],我从Perl脚本接收到json_encoded数据,我在我的PHP应用程序中使用它。 我想要添加的内容:Perl脚本正在通过网站收集信息,具体取决于其初始调用中传递的参数。我想向PHP应用程序发送一个文本字符串,我可以将其显示为一种进度条。 我认为应该如何做:我希望每秒轮询(每1-2秒)已设置的“progression”更新通道。我将使用Javascript / jQuery编写到html div容器中供用户查看。我认为我不应将“进度”通道与更重要的“json_encode(data)”通道混合在一起,因为那样我就需要解密stdout流。 (这种想法是否合乎逻辑,实用?) 我的主要问题:如何使用其他“文件描述符号?”我认为设置其他通道是很简单的,例如下面的3 => ...:
$tunnels = array(   
   0 => array('pipe', 'r'),     
   1 => array('pipe', 'w'),    
   2 => array('pipe', 'w'),    
   3 => array('pipe', 'w')        
);

$io = array();
$resource = proc_open("perl file/tomy/perl/code.pl $param1 $param2 $param3", $tunnels, $io);

if(!is_resource($resource)) {
    $error = "No Resource";
}

fclose($io[0]);

$perlOutput = stream_get_contents($io[1]);
$output = json_decode($perlOutput);

$errors = stream_get_contents($io[2]);
print "$errors<p>";

fclose($io[1]);
fclose($io[2]);

$result = proc_close($resource);

if($result != 0) {
    echo "you returned a $result result on proc_close";
}

但是,在Perl脚本中,我只需要像这样写入标准输出:
my $json_terms = encode_json(\@terms);
print $json_terms;

如果我正确理解了设置额外通道的过程(如上所述,3 => …),那么我该如何在Perl脚本中向其写入内容?
谢谢。
2个回答

2

您需要打开一个新的文件句柄,并将其复制到文件描述符3:

open STD3, '>&3';
print STDERR "foo\n";
print STD3   "bar\n";

$ perl script.pl 2> file2 3> file3
$ cat file2
foo
$ cat file3
bar

编辑:根据Greg Bacon的评论,open STD3,'>&=', 3open STD3,'>&=3'直接打开文件描述符,类似于C的fdopen调用,避免了dup调用,节省了一个文件描述符。


更直接的方式是 open my $fh, ">&=", 3 - Greg Bacon
Mob,有你和@Greg_Bacon的帮助,我已经让php<->perl通信了。谢谢。但是,即使我让管道变热,我在Web页面上仍然无法写入div,直到ajax post完成-它调用了php,然后proc_opens perl脚本。我试图提供有关perl脚本进度的状态更新。长轮询或webSocket是我最好的选择吗?谢谢你的帮助! - Ricalsin

2

假设你想要监控一个hello-world程序的进度,其中每一步都会将一个点写入指定的文件描述符。

#! /usr/bin/env perl

use warnings;
use strict;

die "Usage: $0 progress-fd\n" unless @ARGV == 1;

my $fd = shift;
open my $progress, ">&=", $fd or die "$0: dup $fd: $!";

# disable buffering on both handles
for ($progress, *STDOUT) {
  select $_;
  $| = 1;
}

my $output = "Hello, world!\n";

while ($output =~ s/^(.)(.*)\z/$2/s) {
  my $next = $1;
  print $next;
  print $progress ".";
  sleep 1;
}

使用bash语法在/tmp/progress上打开fd 3并将其连接到程序中,命令如下:
$ (exec 3>/tmp/progress; ./hello-world 3)
Hello, world!

$ cat /tmp/progress
..............
(实时查看更有趣。)
为了在终端上实时查看这些点,您可以打开进度描述符,并将其有效地dup2到标准错误输出流上——再次使用bash语法,现场更加有趣。
$ (exec 17>/dev/null; exec 17>&2; ./hello-world 17)
H.e.l.l.o.,. .w.o.r.l.d.!.
.
当你想获取相同效果时,你当然可以跳过额外的步骤:
$ (exec 17>&2; ./hello-world 17)
如果您的Perl程序出现错误,例如:
$ ./hello-world 333
./hello-world: dup 333: Bad file descriptor at ./hello-world line 9.
那么,在PHP一侧的管道写端可能已经设置了它的close-on-exec标志。

聪明。我不确定$ (exec 17>&2; ./hello-world 17)是什么意思。但现在我已经将php和perl脚本之间的通道管道化了,我意识到我被困在一个异步ajax调用中,这个调用是由调用perl脚本的php脚本发起的。我想我需要一个WebSocket(?)。我很快会标记你或@mob的回复。我感谢你们两个的帮助。谢谢! - Ricalsin
第一部分使fd 17与fd 2相同(即终端的标准错误),然后我们告诉hello-world将进度指示器写入fd 17。由于我们设置管道的方式,点号也会显示在终端上。mob很棒,给mob打勾。 - Greg Bacon

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