2012-05-26 15 views
13

मैं एक पर्ल नोब हूं, इसलिए कृपया इस मूल प्रश्न से क्षमा करें। मुझे मौजूदा पर्ल प्रोग्राम को संशोधित करने की आवश्यकता है। मैं एक बाहरी प्रोग्राम के माध्यम से एक स्ट्रिंग (जिसमें एकाधिक लाइनें हो सकती हैं) पाइप करना चाहते हैं और इस प्रोग्राम से आउटपुट पढ़ना चाहते हैं। तो इस बाहरी प्रोग्राम स्ट्रिंग को संशोधित करने के लिए प्रयोग किया जाता है। आइए फिल्टर फ़िल्टर के रूप में बस cat का उपयोग करें। मैंने इसे इस तरह की कोशिश की लेकिन यह काम नहीं करता है। (cat के आउटपुट के बजाय stdout 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

पूरी तरह से काम करता है। धन्यवाद। ओपन 3 के बजाय प्रयुक्त ओपन 2 क्योंकि मुझे stderr की परवाह नहीं है। – kayahr

+2

और यह ध्यान देने योग्य है कि 'ओपन 2' के पहले दो तर्क 'ओपन 3' के विपरीत विपरीत तरीके हैं! उदाहरण के लिए यह 'open2 (\ * CHLD_OUT, \ * CHLD_IN, 'cat') है। –

8

इसमें सिस्टम प्रोग्रामिंग शामिल है, इसलिए यह एक बुनियादी सवाल से अधिक है। जैसा कि लिखा गया है, आपके मुख्य कार्यक्रम को बाहरी कार्यक्रम के साथ पूर्ण-डुप्लेक्स इंटरैक्शन की आवश्यकता नहीं है। Dataflow एक दिशा, अर्थात्

स्ट्रिंग बाहरी कार्यक्रम → मुख्य कार्यक्रम

इस पाइपलाइन का निर्माण सीधा है → में यात्रा करता है। पर्ल के open में “Safe pipe opens” section of the perlipc documentation में एक उपयोगी मोड समझाया गया है।

इंटरप्रोसेस संचार के लिए एक और दिलचस्प दृष्टिकोण आपके एकल कार्यक्रम को मल्टीप्रोसेस बना रहा है और आपके बीच भी संवाद कर रहा है। open फ़ंक्शन एक बहुत ही रोचक चीज़ करने के लिए "-|" या "|-" की फ़ाइल तर्क स्वीकार करेगा: यह आपके द्वारा खोले गए फ़ाइलहेडल से जुड़े बच्चे को फोर्क करता है। बच्चा माता-पिता के समान कार्यक्रम चला रहा है। उदाहरण के लिए, एक अनुमानित यूआईडी या जीआईडी ​​के तहत चलते समय फ़ाइल को सुरक्षित रूप से खोलने के लिए यह उपयोगी है। यदि आप शून्य पर पाइप खोलते हैं, तो आप खोले गए फ़ाइलहेडल को लिख सकते हैं और आपका बच्चा इसे अपने STDIN में ढूंढ पाएगा। यदि आप शून्य से पाइप खोलते हैं, तो आप जिस फ़ाइल को अपने बच्चे को STDOUT पर लिखते हैं, आपने खोले गए फ़ाइलहेडल से पढ़ सकते हैं।

यह एक open है जिसमें एक पाइप शामिल है, जो वापसी मूल्य को समझता है। perlfunc documentation on open बताता है।

आप कमांड - पर एक पाइप को खोलते हैं, एक अंतर्निहित fork किया जाता है, तो खुला रिटर्न दो बार (कि, या तो |- या -|open की एक या दो तर्क रूपों के साथ निर्दिष्ट किया जाता है): में अभिभावक प्रक्रिया यह बाल प्रक्रिया की पिड देता है, और बच्चे की प्रक्रिया में यह (एक परिभाषित) 0 देता है। open सफल था या नहीं, यह निर्धारित करने के लिए defined($pid) या // का उपयोग करें।

मचान बनाने के लिए, हम सही-से-बाएँ क्रम हर कदम पर openfork करने के लिए एक नई प्रक्रिया का उपयोग करने में काम करते हैं।

  1. आपका मुख्य कार्यक्रम पहले से चल रहा है।
  2. अगला, fork एक प्रक्रिया जो अंततः बाहरी कार्यक्रम बन जाएगी।
  3. चरण 2
    1. पहले fork स्ट्रिंग-मुद्रण प्रक्रिया से प्रक्रिया के अंदर
    2. इतनी के रूप में इसके उत्पादन हमारे STDIN पर पहुंच बनाने के लिए।
    3. फिर exec बाहरी परिवर्तन इसके प्रदर्शन को करने के लिए।
  4. स्ट्रिंग-प्रिंटर अपना काम करता है और फिर exit, जो अगले स्तर तक जाता है।
  5. मुख्य कार्यक्रम में वापस, परिवर्तित परिणाम पढ़ें।

उस सभी सेट अप के साथ, आपको बस इतना करना है कि नीचे दिए गए सुझाव, श्री कोब।

#! /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 कमांडलाइन स्विच का उपयोग कर सकते प्रभावी रूप से थोड़ी देर के पाश में अपने मौजूदा प्रोग्राम कोड रैप करने के लिए ... -n के लिए आदमी पृष्ठ देख रहे हैं:

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

तो आप उपयोग कर सकते ऑपरेटिंग सिस्टम के पाइप तंत्र सीधे

cat file | your_perl_prog.pl 

(संपादित) मैं समझाने के लिए यह अधिक ध्यान से ...

प्रश्न स्पष्ट नहीं है क्या हिस्सा पी के बारे में है की कोशिश करेंगे erl प्रोग्राम नाटकों: फ़िल्टर या अंतिम चरण। यह किसी भी मामले में काम करता है, इसलिए मुझे लगता है कि यह बाद वाला है।

'your_perl_prog.pl' आपका मौजूदा कोड है। मैं आपके फ़िल्टर प्रोग्राम 'फ़िल्टर' को कॉल करूंगा।

संशोधित your_perl_prog.pl ताकि कुटिया लाइन एक '-n' स्विच जोड़ा गया है:! #/Usr/bin/perl -n या #/bin/env "पर्ल -n"

यह प्रभावी रूप से

जोड़ने your_perl_prog.pl में कोड के आसपास थोड़ी देर के (<>) {} पाश डालता एक BEGIN ब्लॉक हैडर मुद्रित करने के लिए:

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

आप '$line = <>;' और इस प्रक्रिया/प्रिंट

साथ प्रत्येक पंक्ति पढ़ सकते हैं

फिर साथ

cat sourcefile |filter|your_perl_prog.pl 
+0

कृपया डाउन-वोट की व्याख्या करें ताकि मैं – Rondo

+0

उत्तर की रक्षा कर सकूं। मैंने यह समझाने के लिए एक संपादन जोड़ा जो मेरा मतलब था – Rondo

1

मैं इसे बदले बिना @Greg बेकन के जवाब पर विस्तार करना चाहते बहुत आह्वान।

मैं कुछ इसी तरह अमल करने के लिए किया था, लेकिन बिना कोड चाहते थे दिया/जब आदेश, और यह भी पाया स्पष्ट बाहर निकलने नहीं थी() कॉल नमूना कोड के माध्यम से यह गिर गया और बाहर निकल गया क्योंकि लापता।

मुझे इसे एक्टिवस्टेट पर्ल, चलाने वाले संस्करण पर भी काम करना पड़ा, लेकिन पर्ल का वह संस्करण काम नहीं करता है। इस सवाल 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: $!"; 
संबंधित मुद्दे