2008-12-22 39 views
18

Đây là một kịch bản. Bạn có một số lượng lớn tập lệnh cũ, tất cả đều sử dụng một thư viện chung. Các tập lệnh được sử dụng sử dụng câu lệnh 'in' cho đầu ra chẩn đoán. Không có thay đổi nào được cho phép đối với các tập lệnh - chúng có phạm vi rộng và rộng, có sự chấp thuận của chúng và từ lâu đã rời khỏi các thung lũng có hiệu quả về giám sát và kiểm soát.Làm thế nào tôi có thể móc vào bản in của Perl?

Bây giờ, một nhu cầu mới đã đến: việc ghi nhật ký bây giờ phải được thêm vào thư viện. Điều này phải được thực hiện tự động và minh bạch, mà không cần người dùng của thư viện chuẩn cần thay đổi tập lệnh của họ. Các phương thức thư viện phổ biến có thể chỉ đơn giản là có các cuộc gọi ghi nhật ký được thêm vào chúng; đó là phần dễ dàng. Phần cứng nằm trong thực tế là đầu ra chẩn đoán từ các tập lệnh này luôn được hiển thị bằng cách sử dụng câu lệnh 'in'. Đầu ra chẩn đoán này phải được lưu trữ, nhưng cũng quan trọng, được xử lý.

Ví dụ về quy trình xử lý này, thư viện chỉ nên ghi lại các dòng in có chứa các từ 'cảnh báo', 'lỗi', 'thông báo' hoặc 'chú ý'. Dưới đây cực kỳ không đáng kể và giả tạo Mã Ví dụ (tm) sẽ ghi lại một số đầu ra nói:

sub CheckPrintOutput 
{ 
    my @output = @_; # args passed to print eventually find their way here. 
    foreach my $value (@output) { 
     Log->log($value) if $value =~ /warning|error|notice|attention/i; 
    } 
} 

(Tôi muốn tránh các vấn đề như 'những gì thực sự cần phải đăng nhập', 'in không nên được sử dụng cho chẩn đoán ',' perl sucks ', hoặc' ví dụ này có lỗ hổng xy và z '... điều này được đơn giản hóa rất nhiều cho ngắn gọn và rõ ràng.)

Vấn đề cơ bản đi xuống để thu thập và xử lý dữ liệu được truyền đến in (hoặc bất kỳ nội trang perl nào, cùng với các dòng lập luận đó). Có thể không? Có cách nào để làm sạch không? Có bất kỳ mô-đun đăng nhập nào có móc để cho phép bạn làm điều đó không? Hay nó là thứ gì đó nên tránh như bệnh dịch hạch, và tôi nên từ bỏ việc bắt giữ và xử lý đầu ra đã in?

Bổ sung: Điều này phải chạy nhiều nền tảng - windows và * nix. Quá trình chạy các tập lệnh phải giữ nguyên, giống như đầu ra của tập lệnh.

bổ sung thêm: Một gợi ý thú vị làm trong các ý kiến ​​của câu trả lời codelogic của:

Bạn có thể phân lớp http://perldoc.perl.org/IO/Handle.html và tạo xử lý tập tin riêng của bạn mà sẽ làm công việc khai thác gỗ. - Kamil Kisiel

Điều này có thể làm điều đó, hãy cẩn thận với hai:

1) Tôi muốn cần một cách để xuất khẩu chức năng này cho bất cứ ai sử dụng thư viện chung. Nó sẽ phải tự động áp dụng cho STDOUT và có thể là STDERR.

2) the IO::Handle tài liệu nói rằng bạn không thể phân loại nó và nỗ lực của tôi cho đến nay đã không kết quả. Có điều gì đặc biệt cần thiết để làm cho sublclassing IO :: Xử lý công việc? Tiêu chuẩn 'sử dụng cơ sở' IO :: Xử lý 'và sau đó ghi đè các phương pháp mới/in dường như không làm gì cả.

Chỉnh sửa cuối cùng: Có vẻ như IO :: Xử lý là kết thúc chết, nhưng Tie :: Xử lý có thể thực hiện. Cảm ơn tất cả những lời đề nghị; tất cả đều thực sự tốt. Tôi sẽ thử Tie :: Handle route. Nếu nó gây ra vấn đề tôi sẽ trở lại!

Phụ lục: Lưu ý rằng sau khi làm việc với điều này một chút, tôi thấy rằng Tie :: Xử lý sẽ hoạt động, nếu bạn không làm bất cứ điều gì phức tạp. Nếu bạn sử dụng bất kỳ tính năng nào của IO :: Xử lý với STDOUT gắn hoặc STDERR của bạn, về cơ bản nó là một crapshoot để làm cho chúng hoạt động đáng tin cậy - Tôi không thể tìm cách để có được phương pháp autoflush của IO :: Xử lý để làm việc trên gắn của tôi xử lý. Nếu tôi bật autoflush trước khi tôi gắn tay cầm nó sẽ hoạt động.Nếu điều đó phù hợp với bạn, tuyến đường Tie :: Handle có thể chấp nhận được.

+0

Vậy điều gì * là * bạn được phép thay đổi? Dòng lệnh? Tệp tham số? Ví dụ: giả sử tôi đã nói "Có thể bạn có thể móc in", phạm vi của những gì bạn có thể làm là gì? – Axeman

+0

Tôi có thể thay đổi bất kỳ thứ gì trong thư viện chung. Người dùng không nhất thiết phải chạy các kịch bản của mình theo bất kỳ cách nào khác hoặc phải chuyển bất kỳ thứ gì mới vào trên dòng lệnh. Luồng dữ liệu cuối cùng trên STDOUT và STDERR phải giống như trước đây. –

+0

điều gì xảy ra với đầu ra ban đầu? bạn có thể tail -f nó và xử lý từ đó? – user44511

Trả lời

24

Có một số bản dựng sẵn mà bạn có thể ghi đè (xem perlsub). Tuy nhiên, print là một trong những trình cài sẵn không hoạt động theo cách này. Những khó khăn của việc ghi đè print được nêu chi tiết tại số perlmonk's thread này.

Tuy nhiên, bạn có thể

  1. Tạo một gói
  2. Tie một tay cầm
  3. Chọn xử lý này.

Bây giờ, một vài người đã đưa ra khuôn khổ cơ bản, nhưng nó hoạt động ra loại như thế này:

package IO::Override; 
use base qw<Tie::Handle>; 
use Symbol qw<geniosym>; 

sub TIEHANDLE { return bless geniosym, __PACKAGE__ } 

sub PRINT { 
    shift; 
    # You can do pretty much anything you want here. 
    # And it's printing to what was STDOUT at the start. 
    # 
    print $OLD_STDOUT join('', 'NOTICE: ', @_); 
} 

tie *PRINTOUT, 'IO::Override'; 
our $OLD_STDOUT = select(*PRINTOUT); 

Bạn có thể ghi đè lên printf trong cùng một cách:

sub PRINTF { 
    shift; 
    # You can do pretty much anything you want here. 
    # And it's printing to what was STDOUT at the start. 
    # 
    my $format = shift; 
    print $OLD_STDOUT join('', 'NOTICE: ', sprintf($format, @_)); 
} 

Xem Tie::Handle để biết tất cả những gì bạn có thể ghi đè lên hành vi của STDOUT.

+0

Ồ, điều này có vẻ tốt. Điều này có thay đổi hành vi của in nếu họ sử dụng in để ghi vào tập tin xử lý, hoặc chỉ để STDOUT? –

+0

Nhìn vào nó nhiều hơn. Tôi hiểu - nó gắn liền với một tay cầm tập tin tại một thời điểm, đúng không? Tôi sẽ nhanh chóng quay lại và báo cáo lại. –

+0

"Xử lý tệp" chỉ là một phương tiện. Khi chúng ta có luồng hướng đến nơi chúng ta có thể xác định hành vi, chúng ta có thể ghi ra nhiều xử lý thực như chúng ta muốn, lọc nó theo ý muốn - thậm chí thực hiện các cuộc gọi cơ sở dữ liệu, nếu đó là những gì chúng ta muốn làm. – Axeman

8

Bạn có thể sử dụng số select của Perl để chuyển hướng STDOUT.

open my $fh, ">log.txt"; 
print "test1\n"; 
my $current_fh = select $fh; 
print "test2\n"; 
select $current_fh; 
print "test3\n"; 

Xử lý tệp có thể là bất kỳ thứ gì, ngay cả đường ống đến quy trình khác xử lý thông điệp tường trình của bạn.

PerlIO::tee trong mô-đun PerlIO::Util dường như cho phép bạn 'phát' đầu ra của một tệp xử lý đến nhiều đích (ví dụ: bộ xử lý nhật ký và STDOUT).

+0

chọn là tốt và tốt, nhưng chúng ta cũng cần phải xử lý dữ liệu đó. Có một loại xử lý tập tin mà cho phép móc để đánh giá và làm một cái gì đó với các dữ liệu được truyền cho nó? –

+0

Ngoài ra, chúng tôi không thể sửa đổi hành vi hiện tại của các tập lệnh này, vì vậy đầu ra phải còn trên STDOUT. –

+0

Bạn có thể mở một filehandle như một đường ống cho quá trình khác. Làm các công cụ đăng nhập trong đó, sau đó bạn có thể in để stdout từ bên trong nó. – flussence

-1

Bạn có thể chạy tập lệnh từ tập lệnh trình bao bọc để thu thập lệnh xuất bản của tập lệnh gốc và viết đầu ra ở đâu đó hợp lý.

+0

Thật không may, điều này vi phạm 'kịch bản phải chạy giống như yêu cầu trước. Người dùng không phải làm bất cứ điều gì khác nhau để có được thông tin đăng nhập này. –

+0

Nó không vi phạm bất cứ điều gì. Không có gì nói rằng điều mà thực hiện khi bạn gõ 'perl' là perl thực sự. –

+0

Đúng từ quan điểm thực hiện chung; một kịch bản trình bao bọc là một giải pháp khả thi. Tuy nhiên, yêu cầu người dùng chạy một tập lệnh khác chỉ để chạy tập lệnh của họ không phải là giải pháp sẽ hoạt động trong trường hợp này - dòng lệnh, phân phối perl và đầu ra của bàn điều khiển phải giữ nguyên. –

6

Rất nhiều lựa chọn. Sử dụng select() để thay đổi filehandle in mặc định. Hoặc buộc STDOUT. Hoặc mở lại. Hoặc áp dụng một lớp IO cho nó.

3

Đây không phải là câu trả lời cho vấn đề của bạn nhưng bạn sẽ có thể áp dụng logic để sử dụng cho riêng bạn. Nếu không, có thể ai đó sẽ thấy nó hữu ích.

bắt đầu bị thay đổi trước khi chúng xảy ra ...

package PsychicSTDOUT; 
use strict; 

my $c = 0; 
my $malformed_header = 0; 
open(TRUE_STDOUT, '>', '/dev/stdout'); 
tie *STDOUT, __PACKAGE__, (*STDOUT); 

sub TIEHANDLE { 
    my $class = shift; 
    my $handles = [@_]; 
    bless $handles, $class; 
    return $handles; 
} 

sub PRINT { 
    my $class = shift; 
    if (!$c++ && @_[0] !~ /^content-type/i) { 
     my (undef, $file, $line) = caller; 
     print STDERR "Missing content-type in $file at line $line!!\n"; 
     $malformed_header = 1; 
    } 
    return 0 if ($malformed_header); 
    return print TRUE_STDOUT @_; 
} 
1; 

sử dụng:

use PsychicSTDOUT; 
print "content-type: text/html\n\n"; #try commenting out this line 
print "<html>\n"; 
print "</html>\n"; 
Các vấn đề liên quan