2017-05-04 10 views
0

다음 코드를 실행하면 마지막 서버가 인쇄되지 않습니다. 두 번째 마지막 배열 요소 다음에 스크립트가 멈 춥니 다.Perl 파이프 하위 프로세스에서 핸들 배열의 마지막 요소에 매달려 읽기

$ ./get_stuck.pl 
92  18196 
93  27420 
94  17635 
95  10258 
96  10831 

서버 '96'이후 '97'출력이 있어야 아직 존재하지 않고, 스크립트가 단지/그 시점에서 정지 응답 :

my %readers; 
my $command = "pgrep -f weblogic.Name"; 

foreach my $server(@servers) { 
    pipe($readers{$server},WRITER); 
    unless(my $pid = fork()) { 
     my $response = qx(ssh -q oracle\@$server "$command"); 
     print WRITER $response; 
     exit(); 
    } 
} 


foreach my $server (@servers) { 
    my $fh = $readers{$server}; 
    my @procs = <$fh>; 
    chomp(@procs); 
    for my $proc (@procs) { 
      printf "%s\t%s\n", substr($server,8), $proc; 
    } 
} 

print "end\n"; 

출력은 다음과 같다. 내가 배열 대신 문자열을 사용하는 독자 섹션을 변경하는 경우

다음과 같이 여러 결과가있는 경우

foreach my $server (@servers) { 
    my $fh = $readers{$server}; 
    my $procs = <$fh>; 
    printf "%s\n", $server; 
} 

가 ... 다음 스크립트는, 그러나, '97'를 포함하여 모든 서버를 인쇄 이 명령은 첫 번째 결과 만 출력합니다 (줄 바꿈을 어기는 것처럼 보입니다). 즉, 명령이 주어진 서버에 대해 3 개의 프로세스 ID를 리턴하면 첫 번째 프로세스 ID 만 인쇄됩니다.

왜 배열을 사용하면 스크립트가 마지막 요소에 멈추는 지에 대한 제안 사항이 있습니까? 또는 문자열을 사용하는 방법이 다소 바람직하지 않지만 모든 결과를 검색 할 수 있습니까?

답변

2

실제로 시도하지는 않았지만 다음과 같습니다.

이 코드는 교착 상태에있는 것 같습니다. 목록 문맥

  • <>는 파일의 끝 (EOF)에 도달 할 때까지 판독 파일 전체, 즉을 slurps.
  • 해당 파일 핸들은 파이프를 참조합니다.
  • 쓰기 끝으로 열린 모든 핸들이 닫히면 파이프의 읽기 끝이 EOF에 도달합니다.
  • 부모는 WRITER을 닫지 않습니다 (자식은 종료시 암시 적으로 닫습니다).
  • 따라서 부모는 쓰기 끝을 열린 상태로 유지하면서 파이프에서 계속 읽습니다.

사실상 마지막 배열 요소에만 해당되는 이유는 일반 파일 핸들 (WRITER)을 사용하고 있기 때문입니다. 사실상 전역 변수입니다. 동일한 핸들을 다시 열면 암시 적으로 닫힙니다. 즉 루프의 (n + 1) 번째 반복은 n 번째 파이프를 닫습니다. 마지막으로 WRITER 만 열어 둡니다.

foreach my $server(@servers) { 
    pipe($readers{$server},WRITER); 
    unless(my $pid = fork()) { 
     my $response = qx(ssh -q oracle\@$server "$command"); 
     print WRITER $response; 
     exit(); 
    } 
    close WRITER; # always close WRITER in the parent 
} 

그러나 나는이에 코드를 변경하는 것이 좋습니다 : 내가 옳다 경우

후 수정은

foreach my $server (@servers) { 
    pipe($readers{$server}, my $WRITER); 
    defined(my $pid = fork()) or die "$0: fork: $!\n"; 
    unless($pid) { 
     my $response = qx(ssh -q oracle\@$server "$command"); 
     print $WRITER $response; 
     exit(); 
    } 
    close $WRITER; 
} 

, 즉 fork에 오류가 있는지 확인하고 베어릭 파일 핸들 대신 어휘 변수를 사용하십시오. 이 경우 close은 다른 참조가 없으므로 $WRITER이 범위 끝 (현재 루프 반복)에서 암시 적으로 닫히기 때문에 실제로 선택 사항입니다.당신이 열려 파이프를 사용하여 좀 더를 단순화 할 수

: 마지막으로

foreach my $server (@servers) { 
    open $readers{$server}, '-|', 'ssh', '-q', "oracle\@$server", $command 
     or die "$0: ssh: $!\n"; 
} 

my $fh = $readers{$server}; 
my @procs = <$fh>; 

이 (내가 싫어

my @proces = readline $readers{$server}; 

로 감소 될 수있다 <> 연산자 제 의견으로는 언제나 readline 또는 glob이 명시 적으로 더 읽기 쉽도록합니다.)

+0

필자는 'WRITER'도 닫지 않을 것이라고 생각했습니다. 나는 여전히 '<$fh>'에 쓰여진 스칼라가 그것을 어떻게 변화시키는지를 보지 못했다. 나는 그것을 어디 선가 놓치고 있는가? – zdim

+0

@zdim 스칼라 컨텍스트에서는 첫 번째 줄만 가져옵니다 (또는 OP 명령에서 "* 그러나 명령에서 여러 결과가있는 경우 첫 번째 결과 만 출력됩니다 (줄 바꿈이 중단 된 것처럼 보입니다) *"). 반면에 목록 컨텍스트는 모든 행을 읽으므로 EOF에 도달 할 때까지 차단이 필요합니다. – melpomene

+0

우수! 나는 WRITER를 닫으려고했으나 그 후에는'unless' 문 안에서했습니다. 제안한대로 어휘 변수를 사용하도록 코드를 업데이트했지만 명확성을 위해 명시 적 close 문을 포함했습니다. 도와 주셔서 대단히 감사합니다. 챔피언. – Stu