Вопрос по perl, pipe – Как читать и писать из трубы в Perl?

14

Я новичок в Perl, поэтому прошу прощения за этот основной вопрос. Мне нужно изменить существующую программу Perl. Я хочу передать строку (которая может содержать несколько строк) через внешнюю программу и прочитать вывод этой программы. Так что эта внешняя программа используется для изменения строки. Давайте просто использоватьcat в качестве программы фильтра. Я попробовал это так, но это не работает. (Выводcat идет на стандартный вывод вместо того, чтобы быть прочитаннымperl.)

#!/usr/bin/perl

open(MESSAGE, "| cat |") or die("cat failed\n");
print MESSAGE "Line 1\nLine 2\n";
my $message = "";
while (<MESSAGE>)
{
    $message .= $_;
}
close(MESSAGE);
print "This is the message: $message\n";

Я читал, что это не поддерживается Perl, потому что он может зайти в тупик, и я могу это понять. Но как мне тогда это сделать?

perlipc Страница руководства содержит обсуждение и множество примеров различных подходов. tripleee

Ваш Ответ

4   ответа
2

чтобы эффективно обернуть существующий программный код в цикл while ... посмотрите на справочную страницу для -n:

 LINE:
            while (<>) {
                ...             # your program goes here
            }

Затем вы можете напрямую использовать механизм конвейера операционной системы.

cat file | your_perl_prog.pl

(Редактировать) Я попытаюсь объяснить это более тщательно ...

Неясно, какую роль играет Perl-программа: фильтр или финальная стадия. Это работает в любом случае, поэтому я предполагаю, что это последнее.

& APOS; your_perl_prog.pl & APOS; это ваш существующий код Я назову вашу программу-фильтр «фильтром».

Измените your_perl_prog.pl так, чтобы в строке shebang был добавлен '-n'; переключатель: #! / usr / bin / perl -n или #! / bin / env & quot; perl -n & quot;

Это эффективно создает цикл while (& lt; & gt;) {} вокруг кода в your_perl_prog.pl

добавьте блок BEGIN для печати заголовка:

BEGIN {print "HEADER LINE\n");}

Вы можете прочитать каждую строку с'$line = <>;' и обрабатывать / печатать

Затем вызовите лот с

cat sourcefile |filter|your_perl_prog.pl
Я добавил редактирование, чтобы объяснить, что я имел в виду
Пожалуйста, объясните отрицательное голосование, чтобы я мог защитить ответ
24

IPC::Open3 добиться двунаправленного общения с ребенком.

use strict;
use IPC::Open3;

my $pid = open3(\*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, 'cat')
    or die "open3() failed $!";

my $r;

for(my $i=1;$i<10;$i++) {
    print CHLD_IN "$i\n";
    $r = <CHLD_OUT>;
    print "Got $r from child\n";
}
Работает отлично. Благодарю. Использовал open2 вместо open3, потому что я не забочусь о stderr. kayahr
И стоит отметить, что первые два аргументаopen2 противоположный путь кopen3! Это & APOS; sopen2(\*CHLD_OUT, \*CHLD_IN, 'cat') например.
1

Я должен был выполнить что-то подобное, но хотел кодировать без заданные / когда команды, а также найденные там были явные exit () пропущенные вызовы, потому что в примере кода он провалился и вышел.

Я также должен был заставить его работать на версии с ActiveState Perl, но эта версия Perl не работает. Смотрите этот вопросКак читать и писать из канала в Perl с ActiveState Perl?

#! /usr/bin/env perl

use strict;
use warnings;

my $isActiveStatePerl = defined(&Win32::BuildNumber);

sub pipeFromFork
{
    return open($_[0], "-|") if (!$isActiveStatePerl);
    die "active state perl cannot cope with dup file handles after fork";

    pipe $_[0], my $child or die "cannot create pipe";
    my $pid = fork();
    die "fork failed: $!" unless defined $pid;
    if ($pid) {         # parent
        close $child; 
    } else {            # child 
        open(STDOUT, ">&=", $child) or die "cannot clone child to STDOUT";
        close $_[0];
    }
    return $pid;
}


my @transform = qw( tr [A-Za-z] [N-ZA-Mn-za-m] );  # rot13
my @inception = (
  "V xabj, Qnq. Lbh jrer qvfnccbvagrq gung V pbhyqa'g or lbh.",
  "V jnf qvfnccbvagrq gung lbh gevrq.",
);

sub snow_fortress { print map "$_\n", @inception }

sub hotel 
{
    my $fh;
    my $pid = pipeFromFork($fh);     # my $pid = open STDIN, "-|";
    defined($pid)  or die "$0: fork: $!";
    if (0 == $pid) {
        snow_fortress;
        exit(0);
    }
    open(STDIN, "<&", $fh)  or  die "cannot clone to STDIN";
    exec @transform or die "$0: exec: $!";
}

my $fh;
my $pid = pipeFromFork($fh);            # my $pid = open my $fh, "-|";
defined($pid) or die "$0: fork: $!";
if (0 == $pid) {
    hotel;
    exit(0);
}

print while <$fh>;
close $fh or warn "$0: close: $!";
10

так что это больше, чем простой вопрос. Как написано, ваша основная программа не требует полнодуплексного взаимодействия с внешней программой. Поток данных движется в одном направлении, а именно

строка & # x2192; внешняя программа & # x2192; основная программа

Создать этот конвейер просто. Perl & # x2019; sopen имеет полезный режим, описанный в& # x201C; Безопасная труба открывается & # x201D; раздел документации perlipc.

Another interesting approach to interprocess communication is making your single program go multiprocess and communicate between—or even amongst—yourselves. The open function will accept a file argument of either "-|" or "|-" to do a very interesting thing: it forks a child connected to the filehandle you’ve opened. The child is running the same program as the parent. This is useful for safely opening a file when running under an assumed UID or GID, for example. If you open a pipe to minus, you can write to the filehandle you opened and your kid will find it in his STDIN. If you open a pipe from minus, you can read from the filehandle you opened whatever your kid writes to his STDOUT.

Этоopen это включает в себя канал, который дает нюанс возвращаемого значения.Perlfunc документация поopen объясняет.

If you open a pipe on the command - (that is, specify either |- or -| with the one- or two-argument forms of open), an implicit fork is done, so open returns twice: in the parent process it returns the pid of the child process, and in the child process it returns (a defined) 0. Use defined($pid) or // to determine whether the open was successful.

Для создания лесов мы работаем в порядке справа налево, используяopen вfork новый процесс на каждом этапе.

Your main program is already running. Next, fork a process that will eventually become the external program. Inside the process from step 2 First fork the string-printing process so as to make its output arrive on our STDIN. Then exec the external program to perform its transformation. Have the string-printer do its work and then exit, which kicks up to the next level. Back in the main program, read the transformed result.

Со всей этой настройкой все, что вам нужно сделать, это внедрить ваше предложение в нижней части, мистер Кобб.

#! /usr/bin/env perl

use 5.10.0;  # for defined-or and given/when
use strict;
use warnings;

my @transform = qw( tr [A-Za-z] [N-ZA-Mn-za-m] );  # rot13
my @inception = (
  "V xabj, Qnq. Lbh jrer qvfnccbvagrq gung V pbhyqa'g or lbh.",
  "V jnf qvfnccbvagrq gung lbh gevrq.",
);

sub snow_fortress { print map "$_\n", @inception }

sub hotel {
  given (open(STDIN, "-|") // die "$0: fork: $!") {  # / StackOverflow hiliter
    snow_fortress when 0;
    exec @transform or die "$0: exec: $!";
  }
}

given (open(my $fh, "-|") // die "$0: fork: $!") {
  hotel when 0;

  print while <$fh>;
  close $fh or warn "$0: close: $!";
}

Спасибо за возможность написать такую веселую программу!

Похожие вопросы