2010-06-28 26 views
5

Trong Perl, tôi đang cố đọc một tệp nhật ký và chỉ in các dòng có dấu thời gian giữa hai lần cụ thể. Định dạng thời gian là hh: mm: ss và đây luôn là giá trị thứ ba trên mỗi nhật ký. Ví dụ: tôi sẽ tìm kiếm các dòng sẽ nằm trong khoảng từ 12:52:33 đến 12:59:33Làm thế nào để tìm kiếm các dòng trong một tệp giữa hai dấu thời gian sử dụng Perl?

Tôi mới sử dụng Perl và không biết nên bắt đầu tuyến đường nào để bắt đầu lập trình chương trình này. Tôi khá chắc chắn điều này sẽ sử dụng một số loại regex, nhưng đối với cuộc sống của tôi, tôi thậm chí không thể bắt đầu hiểu được những gì sẽ được. Ai đó có thể giúp tôi với điều này.

Ngoài ra, để làm điều này khó khăn hơn, tôi phải thực hiện điều này với mô-đun Perl chính vì công ty của tôi sẽ không cho phép tôi sử dụng bất kỳ mô-đun nào khác cho đến khi chúng được kiểm tra và xác minh. hệ thống tập lệnh có thể tương tác với.

+1

Bạn có ý nghĩa gì với giá trị thứ ba? Trường thứ ba? – radius

+1

Bạn có thể đăng một ví dụ về một dòng từ nhật ký không? Điều đó sẽ giúp mọi thứ rõ ràng. –

Trả lời

2

Trong giả, bạn muốn làm điều gì đó như thế này:

  • đọc trong dòng tập tin bằng cách dòng:
    • phân tích các dấu thời gian cho dòng này.
    • nếu nó nhỏ hơn thời gian bắt đầu, hãy bỏ qua đến dòng tiếp theo.
    • nếu nó lớn hơn thời gian kết thúc, hãy chuyển sang dòng tiếp theo!
    • khác: đây là dòng bạn muốn: in ra.

này có thể quá cao cấp cho nhu cầu của bạn, nhưng flip-flop operator.. ngay lập tức nói đến cái tâm như một cái gì đó mà có thể hữu ích ở đây.

Đối với đọc trong một tập tin từ thiết bị nhập chuẩn, đây là mô hình truyền thống:

while (my $line = <>) 
{ 
    # do stuff... 
} 

Phân tích một dòng vào các lĩnh vực có thể được thực hiện dễ dàng với split (xem perldoc -f split). Bạn có thể sẽ cần phải chia dòng bằng tab hoặc dấu cách, tùy thuộc vào định dạng.

Khi bạn đã có trường cụ thể (chứa dấu thời gian), bạn có thể kiểm tra bằng cách sử dụng regexp tùy chỉnh. Đọc về những người tại perldoc perlre.

Dưới đây là một cái gì đó mà có thể giúp bạn có được gần gũi hơn:

use strict; 
use warnings; 

use POSIX 'mktime'; 
my $starttime = mktime(33, 52, 12); 
my $endtime = mktime(33, 59, 12); 

while (my $line = <>) 
{ 
    # split into fields using whitespace as the delimiter 
    my @fields = split(/\s+/, $line); 

    # the timestamp is the 3rd field 
    my $timestamp = $fields[2]; 

    my ($hour, $min, $sec) = split(':', $timestamp); 
    my $time = mktime($sec, $min, $hour); 

    next unless ($time < $starttime) .. ($time > $endtime); 
    print $line; 
} 
+0

Và nếu bạn muốn O (logN) thay vì O (N), bạn có thể sử dụng tìm kiếm nhị phân thay vì đọc từng dòng (giả sử tệp nhật ký được sắp xếp theo dấu thời gian). – serg

+2

Nhiệm vụ như vậy rất phù hợp với nhà điều hành flip-flop. – Zaid

1

Nếu mỗi dòng trong file có tem thời gian, sau đó trong 'sed' bạn có thể viết:

sed -n '/12:52:33/,/12:59:33/p' logfile 

này sẽ echo các dòng có liên quan.

Có một chương trình Perl, s2p, sẽ chuyển đổi tập lệnh 'sed' thành Perl.

Cấu trúc Perl cơ bản là dọc theo dòng:

my $atfirst = 0; 
my $atend = 0; 
while (<>) 
{ 
    last if $atend; 
    $atfirst = 1 if m/12:52:33/; 
    $atend = 1 if m/12:59:33/; 
    if ($atfirst) 
    { 
     process line as required 
    } 
} 

Lưu ý rằng khi viết, mã sẽ xử lý dòng đầu tiên phù hợp với điểm đánh dấu kết thúc. Nếu bạn không muốn điều đó, hãy di chuyển 'cuối cùng' sau bài kiểm tra.

0

Nếu tệp nhật ký của bạn được phân tách theo ngày, bạn có thể chuyển đổi dấu thời gian thành giây và so sánh các dấu thời gian đó. (Nếu không, sử dụng kỹ thuật này từ my answer to a question you asked earlier.)

Giả sử đăng nhập của bạn là

12:52:32 outside 
12:52:43 strictly inside 
12:59:33 end 
12:59:34 outside

Sau đó, với

#! /usr/bin/perl 

use warnings; 
use strict; 

my $LOGPATH = "/tmp/foo.log"; 

sub usage { "Usage: $0 start-time end-time\n" } 

sub to_seconds { 
    my($h,$m,$s) = split /:/, $_[0]; 
    $h * 60 * 60 + 
     $m * 60 + 
      $s; 
} 

die usage unless @ARGV == 2; 
my($start,$end) = map to_seconds($_), @ARGV; 

open my $log, "<", $LOGPATH or die "$0: open $LOGPATH: $!"; 
while (<$log>) { 
    if (/^(\d+:\d+:\d+)\s+/) { 
    my $time = to_seconds $1; 
    print if $time >= $start && $time <= $end; 
    } 
    else { 
    warn "$0: $LOGPATH:$.: no timestamp!\n"; 
    } 
} 

bạn muốn nhận được đầu ra sau đây:

$ ./between 12:52:33 12:59:33 
12:52:43 strictly inside 
12:59:33 end
2

Nếu thời gian bắt đầu và kết thúc được biết, một lớp lót Perl với một nhà điều hành flip-flop là những gì bạn cần:

perl -ne 'print if /12:52:33/../12:59:33/' logFile 

Nếu có một số logic cơ bản cần thiết để các bạn để xác định sự bắt đầu và kết thúc, sau đó 'tháo vật cuộn tròn lại' one-liner để một kịch bản chính thức:

use strict; 
use warnings; 

open my $log, '<', 'logFile'; 

my $startTime = get_start_time(); # Sets $startTime in hh:mm:ss format 
my $endTime = get_end_time();  # Sets $endTime in hh:mm:ss format 

while (<$log>) { 

    print if /$startTime/../$endTime/; 
} 

Như lưu ý bởi nhận xét của Ether, điều này sẽ thất bại nếu thời gian chính xác không có mặt. Nếu đây là một khả năng, người ta có thể thực hiện logic sau đây thay thế:

use strict; 
use warnings; 
use autosplit; 

open my $log, '<', 'logFile'; 

my $startTime = get_start_time(); # Sets $startTime in hh:mm:ss format 
my $endTime = get_end_time();  # Sets $endTime in hh:mm:ss format 

while (<$log>) { 

    my $time = (split /,/, $_)[2];  # Assuming fields are comma-separated 
             # and timelog is 3rd field 

    last if $time gt $endTime;   # Stop when stop time reached 
    print if $time ge $startTime; 
} 
+1

Điều kiện đó sẽ thất bại nếu không có dòng có dấu thời gian khớp chính xác với thời gian bắt đầu hoặc kết thúc. – Ether

+0

@Ether: Đồng ý. Đây là những gì xảy ra khi OP không chỉ định đầy đủ thông tin về vấn đề này. – Zaid

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