proc_open: расширение номеров файловых дескрипторов для включения обратной связи о состоянии из сценария Perl.

В руководстве PHP proc_open указано:

Номера файловых дескрипторов не ограничены 0, 1 и 2 — вы можете указать любой допустимый номер файлового дескриптора, и он будет передан дочернему процессу. Это позволяет вашему сценарию взаимодействовать с другими сценариями, которые выполняются как «совместные процессы». В частности, это полезно для более безопасной передачи парольных фраз таким программам, как PGP, GPG и openssl. Это также полезно для чтения информации о состоянии, предоставляемой этими программами для вспомогательных файловых дескрипторов.

Что происходит: я вызываю сценарий Perl в веб-приложении на основе PHP и передаю параметры в вызове. У меня нет в будущем необходимости отправлять данные в скрипт. Через стандартный вывод [1] я получаю от Perl-скрипта json_encoded данные, которые использую в своем PHP-приложении.

Что я хотел бы добавить: Perl-скрипт проходит через веб-сайт, собирая информацию в зависимости от параметров, переданных при его первоначальном вызове. Я хотел бы отправить обратно в приложение PHP текстовую строку, которую я мог бы использовать для отображения в виде индикатора выполнения.

Как я думаю, что я должен это сделать: я ожидаю опроса (каждые 1-2 секунды) канала, который был настроен для этого "прогрессивного" обновления. Я бы использовал Javascript/jQuery для записи в контейнер html div для просмотра пользователем. Я не думаю, что мне следует смешивать канал «прогресс» с более важным каналом «json_encode (данные)», поскольку мне тогда нужно будет расшифровать поток 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-скрипта?

Спасибо


person Ricalsin    schedule 24.04.2012    source источник


Ответы (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 для открытия fd 3 на /tmp/progress и подключения его к программе

$ (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.

person Greg Bacon    schedule 24.04.2012
comment
Умная. Я не уверен, что делает $ (exec 17>&2; ./hello-world 17). Но теперь, когда у меня есть каналы между php- и perl-скриптом, я понимаю, что застрял в асинхронном ajax-вызове php-скрипта, который вызывал perl-скрипт. Думаю, мне нужен WebSocket (?). Я скоро отмечу ваш ответ или ответ @mob. Я ценю помощь от вас обоих. Спасибо! - person Ricalsin; 26.04.2012
comment
Первая часть делает fd 17 таким же, как fd 2 (что является стандартной ошибкой терминала), а затем мы говорим hello-world записать индикаторы выполнения в fd 17. Из-за того, как мы настроили сантехнику, точки появляются на терминал тоже. толпа набухает; поставить моб галочку. - person Greg Bacon; 26.04.2012

Вы открываете новый дескриптор файла и копируете его в файловый дескриптор 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

Редактировать: согласно комментарию Грега Бэкона, open STD3, '>&=', 3 или open STD3, '>&=3' открывает дескриптор файла напрямую, как вызов fdopen C, избегая вызова dup и сохраняя дескриптор файла.

person mob    schedule 24.04.2012
comment
Более прямой open my $fh, ">&=", 3. - person Greg Bacon; 25.04.2012
comment
mob, с вашей помощью и помощью @Greg_Bacon у меня есть связь php ‹-› perl. Спасибо. Но даже если я делаю каналы горячими, я все равно не могу писать в div на своей веб-странице, пока не будет выполнен пост ajax, который вызывает php, который proc_opens perl-скрипт. Я пытаюсь дать обновления статуса о том, как работает perl-скрипт. Является ли долгий опрос или веб-сокет двумя лучшими вариантами? Спасибо за вашу помощь! - person Ricalsin; 26.04.2012