2012-05-26 5 views
13

저는 Perl 멍청이입니다. 그래서이 기본적인 질문에 변명하십시오. 기존 Perl 프로그램을 수정해야합니다. 나는 외부 프로그램을 통해 문자열 (여러 줄을 포함 할 수 있음)을 파이프하고이 프로그램의 출력을 읽길 원합니다. 따라서이 외부 프로그램은 문자열을 수정하는 데 사용됩니다. 필터 프로그램으로 간단히 을 사용합시다. 나는 이것 같이 그것을 시도하고 그러나 작동하지 않는다. (cat의 출력은 표준 출력 대신의 perl에 의해 읽을 간다.)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"; 

나는 그것이 교착 상태에 끝낼 수 있으며, 나는 그것을 이해할 수 있기 때문에이 펄에서 지원하지 않는 것을 읽었습니다. 그런데 어떻게해야합니까?

+0

을 참조하십시오 '매뉴얼 페이지는 다른 접근 방식의 예에 대한 논의와 많이 가지고 perlipc'. – tripleee

답변

22

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"; 
} 
+0

완벽하게 작동합니다. 감사. opender 대신 open2를 사용했습니다. 왜냐하면 stderr에 신경 쓰지 않기 때문입니다. – kayahr

+2

그리고'open2'에 대한 처음 두 인자는'open3'과 반대 방향입니다! 그것은'open2 (\ * CHLD_OUT, \ * CHLD_IN, 'cat')'입니다. –

8

여기에는 시스템 프로그래밍이 포함되어 있으므로 기본적인 질문 이상의 것입니다. 서면에 따르면, 주 프로그램은 외부 프로그램과 전이중 상호 작용을 요구하지 않습니다. 데이터 흐름이 한 방향으로, 메인 프로그램

이 파이프 라인을 만들기 간단 → 외부 프로그램 → 즉

문자열로 이동합니다. Perl의 open“Safe pipe opens” section of the perlipc documentation에 설명 된 유용한 모드를 가지고 있습니다.

프로세스 간 통신에 대한 또 다른 흥미로운 접근 방식은 단일 프로그램이 다중 프로세스로 이동하거나 또는 심지어 자신 사이에서 통신하는 것입니다. open 함수는 매우 흥미로운 일을하기 위해 "-|" 또는 "|-"의 파일 인수를 허용합니다. 열린 파일 핸들에 연결된 자식을 분기합니다. 하위 프로그램은 상위 프로그램과 동일한 프로그램을 실행 중입니다. 이는 가정 된 UID 또는 GID로 실행될 때 파일을 안전하게 여는 데 유용합니다. 마이너스로 파이프를 여는 경우 열어 놓은 파일 핸들에 글을 쓸 수 있으며 아이는 STDIN에서 찾을 수 있습니다. 마이너스에서 파이프를 여는 경우, 자녀가 작성한 파일 핸들을 STDOUT에 열어 읽을 수 있습니다.

이것은 반환 값의 뉘앙스를주는 파이프가 관련된 open입니다. perlfunc documentation on open이 설명합니다. 명령 -에 파이프를 열 경우

두 번, 그래서 개방 수익률을 암시 fork 완료, (즉, open의 1 또는 2 인자 형태의 하나 |- 또는 -| 지정됩니다) 다음에 부모 프로세스는 자식 프로세스의 PID를 반환하고 자식 프로세스에서는 반환 된 (정의 된) 0을 반환합니다. open이 성공했는지 확인하려면 defined($pid) 또는 //을 사용하십시오.

의 발판을 만들려면, 우리는 오른쪽에서 왼쪽 순서는 각 단계에서 open fork에 새로운 프로세스를 사용하여 작동합니다.

  1. 주 프로그램이 이미 실행 중입니다.
  2. 다음으로, fork 결국 외부 프로그램이 될 프로세스입니다.출력은 우리 STDIN에 도착 할 수 있도록 2 단계
    1. 먼저 fork 문자열 인쇄 공정에서 공정 내부
    2. .
    3. 그런 다음 exec 변환을 수행 할 외부 프로그램.
  3. 문자열 프린터가 작동하고 다음 수준으로 올라 오는 exit을 수행 했습니까?
  4. 메인 프로그램으로 돌아 가면 변환 된 결과를 읽습니다.

이 모든 것을 설정 했으므로 하단에 제안을 삽입하면됩니다.

#! /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: $!"; 
} 

그런 재미있는 프로그램을 작성해 주셔서 감사합니다!

2
당신은 효과적으로 -n에 대한 man 페이지를 봐 ... 잠시 루프에서 기존 프로그램 코드를 포장하기 위해 -n 명령 줄 스위치를 사용할 수

: 그럼 당신은 사용할 수 있습니다

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

을 운영 체제의 파이프 메커니즘 직접

cat file | your_perl_prog.pl 

(편집) 나는이 더 신중하게 ...

문제는 어떤 부분 페이지에 대해 명확하지 않다 설명하려고합니다 erl 프로그램 재생 : 필터 또는 최종 단계. 이것은 두 경우 모두에서 작동하므로 후자라고 가정합니다.

'your_perl_prog.pl'은 (는) 기존 코드입니다. 귀하의 필터 프로그램 '필터'를 호출하겠습니다.

수정 your_perl_prog.pl 오두막 라인가 스위치 '-n'추가했습니다 있도록! #!는/usr/빈/펄 -n 또는 #/빈/ENV를 "펄 -n"

이를 효과적으로 당신은 '$line = <>;' 및 공정/인쇄

각 라인을 읽을 수

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

:

헤더를 인쇄 할 BEGIN 블록을 추가 your_perl_prog.pl의 코드 주위에 잠시 (<>) {} 루프를 박 았

그럼 내가 그것을 변경하지 않고 @Greg 베이컨의 대답을 확장 할

cat sourcefile |filter|your_perl_prog.pl 
+0

답변을 방어 할 수 있도록 아래 표를 설명해주십시오. – Rondo

+0

나는 의미를 설명하는 편집을 추가했습니다. – Rondo

1

으로 많이 호출합니다.

나는 비슷한 것을 실행해야했지만 주어진/when 명령을 없이 코드하고 싶었고 명시적인 exit() 호출이 누락되었다는 것을 발견했다. 샘플 코드에서 코드가 빠져 나가기 때문이다.

나는 또한 ActiveState perl을 실행하는 버전 인 에서 작동하도록 만들었지 만 perl의 해당 버전은 작동하지 않습니다. 이 질문에 How to read to and write from a pipe in perl with 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: $!"; 
관련 문제