2009-11-17 41 views
9

Tôi hiện đang có một tập lệnh Perl chạy lệnh bên ngoài trên hệ thống, tập hợp kết quả đầu ra và thực hiện một số hành động dựa trên những gì được trả về. Ngay bây giờ, đây là cách tôi chạy này (trong đó $ cmd là một chuỗi với các thiết lập lệnh):Làm cách nào để chạy lệnh hệ thống trong Perl một cách không đồng bộ?

@output = `$cmd`; 

Tôi muốn thay đổi điều này vì vậy nếu bị treo lệnh và không trả lại một giá trị sau khi rất nhiều thời gian sau đó tôi giết lệnh. Làm thế nào tôi sẽ đi về việc chạy này không đồng bộ?

Trả lời

8

Nếu bạn thực sự chỉ cần đặt thời gian chờ trên một cuộc gọi hệ thống nhất định, đó là một vấn đề đơn giản hơn nhiều so với lập trình không đồng bộ.

Tất cả những gì bạn cần là báo động() bên trong khối eval().

Đây là một khối mã mẫu đặt chúng vào một chương trình con mà bạn có thể đưa vào mã của mình. Ví dụ gọi giấc ngủ như vậy là không thú vị cho đầu ra, nhưng không hiển thị cho bạn những chức năng chờ bạn đã quan tâm đến Sản lượng chạy nó là:.

/bin/ngủ 2 thất bại: thời gian chờ tại ./time dòng out 15.

$ cat time-out 
#!/usr/bin/perl 

use warnings; 
use strict; 
my $timeout = 1; 
my @cmd = qw(/bin/sleep 2); 
my $response = timeout_command($timeout, @cmd); 
print "$response\n" if (defined $response); 

sub timeout_command { 
     my $timeout = (shift); 
     my @command = @_; 
     undef [email protected]; 
     my $return = eval { 
       local($SIG{ALRM}) = sub {die "timeout";}; 
       alarm($timeout); 
       my $response; 
       open(CMD, '-|', @command) || die "couldn't run @command: $!\n"; 
       while(<CMD>) { 
         $response .= $_; 
       } 
       close(CMD) || die "Couldn't close execution of @command: $!\n"; 
       $response; 
     }; 
     alarm(0); 
     if ([email protected]) { 
       warn "@cmd failure: [email protected]\n"; 
     } 
     return $return; 
} 
+0

Cảm ơn bạn, đây là những gì tôi đang tìm kiếm. – Joel

+0

Tôi nghĩ bạn sẽ cần \ n trong thông điệp chết. (http://perldoc.perl.org/functions/alarm.html) – ddoxey

12

Có rất nhiều cách để làm điều này:

  • Bạn có thể làm điều này với một fork (perldoc -f nĩa)
  • hoặc sử dụng chủ đề (chủ đề perldoc). Cả hai đều làm cho thông tin trả về trở lại chương trình chính khó khăn.
  • Trên các hệ thống hỗ trợ nó, bạn có thể đặt báo thức (báo động perldoc -f) và sau đó dọn sạch bộ xử lý tín hiệu.
  • Bạn có thể sử dụng vòng lặp sự kiện như POE hoặc Coro.
  • Thay vì backticks, bạn có thể sử dụng open() hoặc tương ứng open2 hoặc open3 (xem IPC :: Open2, IPC :: Open3) để bắt đầu một chương trình trong khi nhận được STDOUT/STDERR thông qua một tập tin xử lý. Chạy các hoạt động đọc không chặn trên đó. (perldoc -f chọn và có thể google "perl nonblocking read")
  • Là một biến thể mạnh mẽ hơn của openX(), hãy xem IPC :: Run/IPC :: Cmd.
  • Có lẽ là tôi không thể nghĩ đến lúc nửa đêm.
+3

trên hệ thống win32 coi chừng vỡ 'select' mà chỉ hoạt động cho ổ cắm và các tương tác có khả năng kỳ lạ với giao diện điều khiển cửa sổ, wperl (chạy perl trong một cửa sổ cách không điều khiển, nhưng có các hiệu ứng lạ trên STDIO và các tiến trình con). Về phía cộng, bạn có thể làm một 'hệ thống (-1, 'foo')'. Ngoài ra, một số cách để bắt đầu một quá trình không thành công sau khi chạy 64 trẻ em. IME, 'Win32 :: Process :: Create()' là cách an toàn nhất để chạy chương trình trong bgrnd. – daotoad

+0

@daotoad, cảm ơn nhận xét đó.Tôi thiếu các đầu mối win32 cho thấy rằng những điều duy nhất tôi biết là ngã ba() được mô phỏng với các chủ đề và bạn không thể sử dụng báo động. Tôi đoán nếu win32 có liên quan, IPC :: Cmd sẽ là một lựa chọn tốt. – tsee

1

Nếu chương trình bên ngoài của bạn không có bất kỳ đầu vào, tìm kiếm những lời sau đây trong perlipc manpage:

Dưới đây là một backtick an toàn hay ống mở để đọc:

Sử dụng mã ví dụ và bảo vệ nó bằng báo thức (cũng được giải thích trong perlipc).

Các vấn đề liên quan