2008-09-20 32 views
56

Có thể chạy một tiến trình bên ngoài từ Perl, nắm bắt stderr, stdout VÀ mã thoát khỏi quá trình không?Làm thế nào để bạn nắm bắt stderr, stdout, và mã thoát tất cả cùng một lúc, trong Perl?

Tôi dường như có thể thực hiện các kết hợp này, ví dụ: sử dụng backticks để có được stdout, IPC :: Open3 để nắm bắt đầu ra và hệ thống() để lấy mã thoát.

Làm thế nào để bạn nắm bắt stderr, stdout và mã thoát cùng một lúc?

Trả lời

26

Nếu bạn đọc lại tài liệu cho IPC :: Open3, bạn sẽ thấy một lưu ý rằng bạn nên gọi waitpid để gặt hái quy trình con. Khi bạn thực hiện việc này, trạng thái sẽ có sẵn trong $?. Giá trị thoát là $? >> 8. Xem $? in perldoc perlvar.

+1

Tôi không biết tại sao các liên kết bị hỏng. Mọi thứ đều ổn trong chỉnh sửa/xem trước. –

+2

Tôi đã gửi perl5porters một bản vá cho IPC :: Open2 và :: Open3 để hiển thị các công cụ waitpid trong SYNOPSIS của các mô-đun đó. :) –

7

Có ba cách cơ bản để chạy các lệnh bên ngoài:

system $cmd;  # using system() 
$output = `$cmd`;  # using backticks (``) 
open (PIPE, "cmd |"); # using open() 

Với system(), cả STDOUT và stderr sẽ đi cùng một vị trí như kịch bản của STDOUTSTDERR, trừ lệnh system() chuyển hướng họ. Backticks và open() chỉ đọc STDOUT lệnh của bạn.

Bạn cũng có thể gọi một số nội dung như sau với mở để chuyển hướng cả hai STDOUTSTDERR.

open(PIPE, "cmd 2>&1 |"); 

Mã trả lại luôn được lưu trữ trong $? như được ghi chú bởi @Michael Carman.

+0

Đừng quên 'qx //' –

41

(Cập nhật: Tôi đã cập nhật API cho IO :: CaptureOutput để làm điều này dễ dàng hơn.)

Có một số cách để làm điều này. Dưới đây là một lựa chọn, sử dụng IO::CaptureOutput mô-đun:

use IO::CaptureOutput qw/capture_exec/; 

my ($stdout, $stderr, $success, $exit_code) = capture_exec(@cmd); 

Đây là capture_exec() chức năng, nhưng IO :: CaptureOutput cũng có một nắm bắt tổng quát hơn() chức năng có thể được sử dụng để chụp một trong hai đầu ra Perl hoặc đầu ra từ các chương trình bên ngoài. Vì vậy, nếu một số mô-đun Perl xảy ra để sử dụng một số chương trình bên ngoài, bạn vẫn nhận được đầu ra. Điều này cũng có nghĩa là bạn chỉ cần nhớ một phương pháp duy nhất để chụp STDOUT và STDERR (hoặc hợp nhất chúng) thay vì sử dụng IPC :: Open3 cho các chương trình bên ngoài và các mô-đun khác để ghi lại đầu ra Perl.

+12

Nó xuất hiện [Capture :: Tiny] (http://search.cpan.org/dist/Capture-Tiny/) là mới hơn và tốt hơn: 'Module này được lấy cảm hứng bởi IO :: CaptureOutput, cung cấp chức năng tương tự mà không có khả năng phát ra đầu ra và với mã và API phức tạp hơn. IO :: CaptureOutput không xử lý các lớp hoặc hầu hết các trường hợp bất thường được mô tả trong phần "Hạn chế" và tôi không còn khuyên dùng nó nữa. ' - http://search.cpan.org/~dagolden/Capture-Tiny-0.18/lib/Capture/Tiny.pm#SEE_ALSO –

+0

Xem http://stackoverflow.com/a/8781408/110126 để biết ví dụ về cách sử dụng Capture :: Tiny. – buzz3791

+5

@PhilipDurbin thật buồn cười khi tác giả của câu trả lời này là cùng một người với tư cách là tác giả của Capture :: Tiny. :) – simbabque

14

Nếu bạn không muốn nội dung của stderr, sau đó chụp() lệnh từ IPC::System::Simple Module này là gần như chính xác những gì bạn đang sau:

use IPC::System::Simple qw(capture system $EXITVAL); 

    my $output = capture($cmd, @args); 

    my $exit_value = $EXITVAL; 

Bạn có thể sử dụng chụp() với một đối số duy nhất để gọi trình bao, hoặc nhiều đối số để tránh trình bao một cách đáng tin cậy. Ngoài ra còn có capturex() mà không bao giờ gọi shell, ngay cả với một đối số duy nhất.

Không giống như các lệnh hệ thống và backticks tích hợp của Perl, IPC :: Hệ thống :: Trả về đơn giản giá trị thoát 32 bit đầy đủ trong Windows. Nó cũng ném một ngoại lệ chi tiết nếu lệnh không thể được khởi động, chết thành tín hiệu hoặc trả về một giá trị thoát không mong muốn.Điều này có nghĩa cho nhiều chương trình, chứ không phải là kiểm tra lối ra giá trị bản thân, bạn có thể dựa vào IPC :: Hệ thống :: đơn giản để làm công việc khó khăn cho bạn:

use IPC::System::Simple qw(system capture $EXIT_ANY); 

system([0,1], "frobincate", @files);  # Must return exitval 0 or 1 

my @lines = capture($EXIT_ANY, "baznicate", @files); # Any exitval is OK. 

foreach my $record (@lines) { 
    system([0, 32], "barnicate", $record); # Must return exitval 0 or 32 
} 

IPC :: Hệ thống :: Đơn giản là tinh khiết Perl, không có phụ thuộc, và hoạt động trên cả hai hệ thống Unix và Windows. Thật không may, nó không cung cấp một cách để chụp STDERR, vì vậy nó có thể không phù hợp cho tất cả các nhu cầu của bạn.

IPC::Run3 cung cấp giao diện sạch và dễ dàng để sửa ống nước chung, nhưng tiếc là không kiểm tra xem lệnh có thành công hay không, vì vậy bạn cần kiểm tra $? theo cách thủ công, điều đó hoàn toàn không vui. Cung cấp giao diện công cộng để kiểm tra $? là cái gì trên to-do list của tôi cho IPC :: Hệ thống :: Đơn giản, vì kiểm tra $? trong một nền tảng thời trang không phải là một nhiệm vụ tôi muốn trên bất cứ ai.

Có các mô-đun khác trong không gian tên IPC:: cũng có thể cung cấp cho bạn trợ giúp. YMMV.

All the best,

Paul

+0

Về mặt kỹ thuật, nó có một sự phụ thuộc, nhưng chỉ trên Windows, nơi mà nó yêu cầu quá trình Win32 ::. Trong khi điều này không có trong lõi Perl, nó được đóng gói với các bản phân phối ActiveState Perl và Strawberry Perl. – xdg

+0

+1, tôi sử dụng IPC :: Hệ thống :: Đơn giản mọi lúc. – Ether

0

Nếu bạn nhận được thực sự phức tạp, bạn có thể muốn thử Expect.pm. Nhưng đó có lẽ là quá mức cần thiết nếu bạn không cần quản lý việc gửi đầu vào cho quá trình này.

0

Tôi đã tìm thấy IPC:run3 rất hữu ích. Bạn có thể chuyển tiếp tất cả các đường ống con đến một glob hoặc một biến; rất dễ dàng! Và mã thoát sẽ được lưu trong $ ?.

Dưới đây là cách tôi lấy stderr mà tôi biết sẽ là một số. Các biến đổi thông tin đầu ra cmd đến stdout (mà tôi đã chuyển đến một tệp trong args bằng cách sử dụng>) và báo cáo có bao nhiêu phép biến đổi thành STDERR.

use IPC::Run3 

my $number; 
my $run = run3("cmd arg1 arg2 >output_file",\undef, \undef, \$number); 
die "Command failed: $!" unless ($run && $? == 0); 
Các vấn đề liên quan