Cách trích xuất nhật ký giữa hai tem thời gian


25

Tôi muốn trích xuất tất cả các bản ghi giữa hai dấu thời gian. Một số dòng có thể không có dấu thời gian, nhưng tôi cũng muốn những dòng đó. Nói tóm lại, tôi muốn mọi dòng rơi dưới hai dấu thời gian. Cấu trúc nhật ký của tôi trông như sau:

[2014-04-07 23:59:58] CheckForCallAction [ERROR] Exception caught in +CheckForCallAction :: null
--Checking user--
Post
[2014-04-08 00:00:03] MobileAppRequestFilter [DEBUG] Action requested checkforcall

Giả sử tôi muốn trích xuất mọi thứ giữa 2014-04-07 23:002014-04-08 02:00.

Xin lưu ý tem thời gian bắt đầu hoặc tem thời gian kết thúc có thể không có trong nhật ký, nhưng tôi muốn mọi dòng giữa hai tem thời gian này.


Bản sao có thể có của stackoverflow.com/questions/7575267/
Ramesh

Bạn chỉ cần làm điều này chỉ một lần hoặc lập trình tại nhiều thời điểm?
Bratchley

Lý do tôi hỏi là bởi vì bạn có thể thực hiện hai grep theo ngữ cảnh (một để lấy mọi thứ sau dấu phân cách bắt đầu và một để dừng in ở dấu phân cách kết thúc) nếu bạn biết các giá trị theo nghĩa đen. Nếu ngày / giờ có thể thay đổi, tou có thể dễ dàng tạo chúng ngay lập tức bằng cách cung cấp đầu vào của người dùng thông qua date -dlệnh và sử dụng điều đó để xây dựng mẫu tìm kiếm.
Bratchley

@Ramesh, câu hỏi được tham khảo quá rộng.
maxschlepzig

@JoelDavis: Tôi muốn làm điều đó bằng lập trình. Vì vậy, mọi lúc tôi chỉ cần nhập dấu thời gian mong muốn để trích xuất nhật ký giữa các dấu thời gian đó ở vị trí / tmp của tôi.
Amit

Câu trả lời:


19

Bạn có thể sử dụng awkcho việc này:

$ awk -F'[]]|[[]' \
  '$0 ~ /^\[/ && $2 >= "2014-04-07 23:00" { p=1 }
   $0 ~ /^\[/ && $2 >= "2014-04-08 02:00" { p=0 }
                                        p { print $0 }' log

Ở đâu:

  • -Fchỉ định các ký tự []dưới dạng dấu tách trường bằng biểu thức chính quy
  • $0 tham chiếu một dòng hoàn chỉnh
  • $2 tham chiếu trường ngày
  • p được sử dụng như là biến boolean bảo vệ việc in ấn thực tế
  • $0 ~ /regex/ là đúng nếu regex khớp $0
  • >=được sử dụng để so sánh chuỗi từ vựng (tương đương với ví dụ strcmp())

Biến thể

Dòng lệnh trên thực hiện khớp thời gian mở đúng . Để có được ngữ nghĩa khoảng thời gian đóng, chỉ cần tăng ngày đúng của bạn, ví dụ:

$ awk -F'[]]|[[]' \
  '$0 ~ /^\[/ && $2 >= "2014-04-07 23:00"    { p=1 }
   $0 ~ /^\[/ && $2 >= "2014-04-08 02:00:01" { p=0 }
                                           p { print $0 }' log

Trong trường hợp bạn muốn khớp dấu thời gian ở định dạng khác, bạn phải sửa đổi $0 ~ /^\[/biểu thức phụ. Lưu ý rằng nó được sử dụng để bỏ qua các dòng mà không có bất kỳ dấu thời gian nào từ logic in / tắt in.

Ví dụ: đối với định dạng dấu thời gian như YYYY-MM-DD HH24:MI:SS(không có []dấu ngoặc), bạn có thể sửa đổi lệnh như sau:

$ awk \
  '$0 ~ /^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-2][0-9]:[0-5][0-9]:[0-5][0-9]/
      {
        if ($1" "$2 >= "2014-04-07 23:00")     p=1;
        if ($1" "$2 >= "2014-04-08 02:00:01")  p=0;
      }
    p { print $0 }' log

(lưu ý rằng dấu tách trường cũng được thay đổi - thành chuyển tiếp trống / không trống, mặc định)


Thanx để chia sẻ tập lệnh nhưng nó không kiểm tra dấu thời gian kết thúc .. Bạn có thể vui lòng kiểm tra. Ngoài ra, hãy cho tôi biết những gì nếu tôi có các bản ghi như 2014-04-07 23:59:58. Ý tôi là không cần niềng răng
Amit

@Amit, đã cập nhật câu trả lời
maxschlepzig

Mặc dù tôi không nghĩ đây là sự cố chuỗi (xem câu trả lời của tôi ), bạn có thể làm cho bạn dễ đọc hơn nhiều và có thể nhanh hơn một chút, bằng cách không lặp lại tất cả các bài kiểm tra: $1 ~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}/ && $2 ~/[0-2][0-9]:[0-5][0-9]:[0-5][0-9]/ { Time = $1" "$2; if (Time >= "2014-04-07 23:00" ) { p=1 } if (Time >= "2014-04-08 02:00:01" ) { p=0 } } p

Xin chào Max, Thêm một nghi ngờ nhỏ nữa .. Nếu tôi có một cái gì đó như Tháng Tư-07-2014 10:51:17. Sau đó, những thay đổi tôi cần làm .. Tôi đã thử code$ 0 ~ / ^ [az | AZ] {4} - [0-9] {2} - [0-9] {4} [0-2] [0-9 ]: [0-5] [0-9]: [0-5] [0-9] / && $ 1 "" $ 2> = "Tháng Tư-07-2014 11:00" {p = 1} $ 0 ~ / ^ [az | AZ] {4} - [0-9] {2} - [0-9] {4} [0-2] [0-9]: [0-5] [0-9]: [0 -5] [0-9] / && $ 1 "" $ 2> = "Tháng Tư-07-2014 12:00:01" {p = 0} codenhưng nó không hoạt động
Amit

@awk_FTW, đã thay đổi mã sao cho regex được chia sẻ rõ ràng.
maxschlepzig

12

Kiểm tra dategreptại https://github.com/mdom/dargetrep

Sự miêu tả:

dargetrep tìm kiếm các tệp đầu vào được đặt tên cho các dòng khớp với phạm vi ngày và in chúng ra thiết bị xuất chuẩn.

Nếu dargetrep hoạt động trên một tệp có thể tìm kiếm, nó có thể thực hiện tìm kiếm nhị phân để tìm dòng đầu tiên và cuối cùng để in khá hiệu quả. dargetrep cũng có thể đọc từ stdin nếu một đối số tên tệp chỉ là dấu gạch nối, nhưng trong trường hợp này, nó phải phân tích từng dòng đơn sẽ chậm hơn.

Ví dụ sử dụng:

dategrep --start "12:00" --end "12:15" --format "%b %d %H:%M:%S" syslog
dategrep --end "12:15" --format "%b %d %H:%M:%S" syslog
dategrep --last-minutes 5 --format "%b %d %H:%M:%S" syslog
dategrep --last-minutes 5 --format rsyslog syslog
cat syslog | dategrep --end "12:15" -

Mặc dù giới hạn này có thể làm cho điều này không phù hợp với câu hỏi chính xác của bạn:

Tại thời điểm này, dargetrep sẽ chết ngay khi tìm thấy một dòng không thể phân tích cú pháp. Trong phiên bản tương lai, nó sẽ có thể cấu hình.


Tôi đã tìm hiểu về lệnh này chỉ một vài ngày trước nhờ onet Breathwell.org/post/81991115668/dargetrep vì vậy, danh tiếng cho anh ấy!
cpugeniusmv

3

Một thay thế cho awkhoặc một công cụ không chuẩn là sử dụng GNU grepcho các greps theo ngữ cảnh của nó. GNU grepsẽ cho phép bạn chỉ định số lượng dòng sau khi khớp tích cực để in -Avà các dòng trước để in với -BVí dụ:

[davisja5@xxxxxxlp01 ~]$ cat test.txt
Ignore this line, please.
This one too while you're at it...
[2014-04-07 23:59:58] CheckForCallAction [ERROR] Exception caught in +CheckForCallAction :: null
--Checking user--
Post
[2014-04-08 00:00:03] MobileAppRequestFilter [DEBUG] Action requested checkforcall
we don't
want these lines.


[davisja5@xxxxxxlp01 ~]$ egrep "^\[2014-04-07 23:59:58\]" test.txt -A 10000 | egrep "^\[2014-04-08 00:00:03\]" -B 10000
[2014-04-07 23:59:58] CheckForCallAction [ERROR] Exception caught in +CheckForCallAction :: null
--Checking user--
Post
[2014-04-08 00:00:03] MobileAppRequestFilter [DEBUG] Action requested checkforcall

Về cơ bản, phần trên nói về grepviệc in 10.000 dòng theo dòng phù hợp với mẫu bạn muốn bắt đầu, thực hiện đầu ra của bạn bắt đầu ở nơi bạn muốn và đi đến cuối (hy vọng) trong khi dòng thứ hai egreptrong đường ống dẫn nó chỉ in dòng với dấu phân cách kết thúc và 10.000 dòng trước nó. Kết quả cuối cùng của hai điều này là bắt đầu từ nơi bạn muốn và sẽ không đi qua nơi bạn bảo nó dừng lại.

10.000 chỉ là con số tôi nghĩ ra, hãy thoải mái đổi nó thành một triệu nếu bạn nghĩ rằng sản lượng của bạn sẽ quá dài.


Làm thế nào điều này sẽ hoạt động nếu không có mục nhật ký cho phạm vi bắt đầu và kết thúc? Nếu OP muốn mọi thứ trong khoảng thời gian từ 14:00 đến 15:00, nhưng không có mục nhật ký nào cho 14:00 thì sao?

Nó sẽ nói về cũng như sedcũng đang tìm kiếm các trận đấu theo nghĩa đen. dategrepcó lẽ là câu trả lời đúng nhất trong tất cả những câu được đưa ra (vì bạn cần có khả năng "mờ" về những dấu thời gian bạn sẽ chấp nhận) nhưng giống như câu trả lời, tôi chỉ đề cập đến nó như một cách thay thế. Điều đó nói rằng, nếu nhật ký hoạt động đủ để tạo ra đầu ra đủ để đảm bảo cắt thì có lẽ nó cũng sẽ có một loại mục nào đó cho đồng hồ đo thời gian nhất định.
Bratchley

0

Sử dụng sed:

#!/bin/bash

E_BADARGS=23

if [ $# -ne "3" ]
then
  echo "Usage: `basename $0` \"<start_date>\" \"<end_date>\" file"
  echo "NOTE:Make sure to put dates in between double quotes"
  exit $E_BADARGS
fi 

isDatePresent(){
        #check if given date exists in file.
        local date=$1
        local file=$2
        grep -q "$date" "$file"
        return $?

}

convertToEpoch(){
    #converts to epoch time
    local _date=$1
    local epoch_date=`date --date="$_date" +%s`
    echo $epoch_date
}

convertFromEpoch(){
    #converts to date/time format from epoch
    local epoch_date=$1
    local _date=`date  --date="@$epoch_date" +"%F %T"`
    echo $_date

}

getDates(){
        # collects all dates at beginning of lines in a file, converts them to epoch and returns a sequence of numbers
        local file="$1"
        local state="$2"
        local i=0
        local date_array=( )
        if [[ "$state" -eq "S" ]];then
            datelist=`cat "$file" | sed -r -e "s/^\[([^\[]+)\].*/\1/" | egrep  "^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}"`
        elif [[ "$state" -eq "E" ]];then
            datelist=`tac "$file" | sed -r -e "s/^\[([^\[]+)\].*/\1/" | egrep  "^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}"`

        else
            echo "Something went wrong while getting dates..." 1>&2
            exit 500
        fi

        while read _date
            do
                epoch_date=`convertToEpoch "$_date"`
                date_array[$i]=$epoch_date
                #echo "$_date" "$epoch_date" 1>&2

            (( i++ ))
            done<<<"$datelist"
        echo ${date_array[@]}   


}

findneighbours(){
    # search next best date if date is not in the file using recursivity
    IFS="$old_IFS"
    local elt=$1
    shift
    local state="$1"
    shift
    local -a array=( "$@" ) 

    index_pivot=`expr ${#array[@]} / 2`
    echo "#array="${#array[@]} ";array="${array[@]} ";index_pivot="$index_pivot 1>&2
    if [ "$index_pivot" -eq 1 -a ${#array[@]} -eq 2 ];then

        if [ "$state" == "E" ];then
            echo ${array[0]}
        elif [ "$state" == "S" ];then
            echo ${array[(( ${#array[@]} - 1 ))]} 
        else
            echo "State" $state "undefined" 1>&2
            exit 100
        fi

    else
        echo "elt with index_pivot="$index_pivot":"${array[$index_pivot]} 1>&2
        if [ $elt -lt ${array[$index_pivot]} ];then
            echo "elt is smaller than pivot" 1>&2
            array=( ${array[@]:0:(($index_pivot + 1)) } )
        else
            echo "elt is bigger than pivot" 1>&2
            array=( ${array[@]:$index_pivot:(( ${#array[@]} - 1 ))} ) 
        fi
        findneighbours "$elt" "$state" "${array[@]}"
    fi
}



findFirstDate(){
    local file="$1"
    echo "Looking for first date in file" 1>&2
    while read line
        do 
            echo "$line" | egrep -q "^\[[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\]" &>/dev/null
            if [ "$?" -eq "0" ]
            then
                #echo "line=" "$line" 1>&2
                firstdate=`echo "$line" | sed -r -e "s/^\[([^\[]+)\].*/\1/"`
                echo "$firstdate"
                break
            else
                echo $? 1>&2
            fi
        done< <( cat "$file" )



}

findLastDate(){
    local file="$1"
    echo "Looking for last date in file" 1>&2
    while read line
        do 
            echo "$line" | egrep -q "^\[[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\]" &>/dev/null
            if [ "$?" -eq "0" ]
            then
                #echo "line=" "$line" 1>&2
                lastdate=`echo "$line" | sed -r -e "s/^\[([^\[]+)\].*/\1/"`
                echo "$lastdate"
                break
            else
                echo $? 1>&2
            fi
        done< <( tac "$file" )


}

findBestDate(){

        IFS="$old_IFS"
        local initdate="$1"
        local file="$2"
        local state="$3"
        local first_elts="$4"
        local last_elts="$5"
        local date_array=( )
        local initdate_epoch=`convertToEpoch "$initdate"`   

        if [[ $initdate_epoch -lt $first_elt ]];then
            echo `convertFromEpoch "$first_elt"`
        elif [[ $initdate_epoch -gt $last_elt ]];then
            echo `convertFromEpoch "$last_elt"` 

        else
            date_array=( `getDates "$file" "$state"` )
            echo "date_array="${date_array[@]} 1>&2
            #first_elt=${date_array[0]}
            #last_elt=${date_array[(( ${#date_array[@]} - 1 ))]}

            echo `convertFromEpoch $(findneighbours "$initdate_epoch" "$state" "${date_array[@]}")`

        fi

}


main(){
    init_date_start="$1"
    init_date_end="$2"
    filename="$3"
    echo "problem start.." 1>&2
    date_array=( "$init_date_start","$init_date_end"  )
    flag_array=( 0 0 )
    i=0
    #echo "$IFS" | cat -vte
    old_IFS="$IFS"
    #changing separator to avoid whitespace issue in date/time format
    IFS=,
    for _date in ${date_array[@]}
    do
        #IFS="$old_IFS"
        #echo "$IFS" | cat -vte
        if isDatePresent "$_date" "$filename";then
            if [ "$i" -eq 0 ];then 
                echo "Starting date exists" 1>&2
                #echo "date_start=""$_date" 1>&2
                date_start="$_date"
            else
                echo "Ending date exists" 1>&2
                #echo "date_end=""$_date" 1>&2
                date_end="$_date"
            fi

        else
            if [ "$i" -eq 0 ];then 
                echo "start date $_date not found" 1>&2
            else
                echo "end date $_date not found" 1>&2
            fi
            flag_array[$i]=1
        fi
        #IFS=,
        (( i++ ))
    done

    IFS="$old_IFS"
    if [ ${flag_array[0]} -eq 1 -o ${flag_array[1]} -eq 1 ];then

        first_elt=`convertToEpoch "$(findFirstDate "$filename")"`
        last_elt=`convertToEpoch "$(findLastDate "$filename")"`
        border_dates_array=( "$first_elt","$last_elt" )

        #echo "first_elt=" $first_elt "last_elt=" $last_elt 1>&2
        i=0
        IFS=,
        for _date in ${date_array[@]}
        do
            if [ $i -eq 0 -a ${flag_array[$i]} -eq 1 ];then
                date_start=`findBestDate "$_date" "$filename" "S" "${border_dates_array[@]}"`
            elif [ $i -eq 1 -a ${flag_array[$i]} -eq 1 ];then
                date_end=`findBestDate "$_date" "$filename" "E" "${border_dates_array[@]}"`
            fi

            (( i++ ))
        done
    fi


    sed -r -n "/^\[${date_start}\]/,/^\[${date_end}\]/p" "$filename"

}


main "$1" "$2" "$3"

Sao chép này trong một tập tin. Nếu bạn không muốn xem thông tin gỡ lỗi, gỡ lỗi được gửi đến stderr, vì vậy chỉ cần thêm "2> / dev / null"


1
Điều này sẽ không hiển thị các tệp nhật ký không có dấu thời gian.
Amit

@Amit, đúng vậy, bạn đã thử chưa?
UnX

@rMistero, nó sẽ không hoạt động vì nếu không có mục nhập nhật ký lúc 22:30, phạm vi sẽ không bị chấm dứt. Như OP đã đề cập, thời gian bắt đầu và dừng có thể không nằm trong nhật ký. Bạn có thể điều chỉnh regex của mình để nó hoạt động, nhưng bạn sẽ mất độ phân giải và không bao giờ được đảm bảo trước rằng phạm vi sẽ chấm dứt vào đúng thời điểm.

@awk_FTW đây là một ví dụ, tôi đã không sử dụng dấu thời gian do Amit cung cấp. Một lần nữa regex có thể được sử dụng. Tôi đồng ý nghĩ rằng nó sẽ không hoạt động nếu dấu thời gian không tồn tại khi được cung cấp rõ ràng hoặc không có biểu thức regex dấu thời gian phù hợp. Tôi sẽ sớm cải thiện nó ..
UnX

"Như OP đã đề cập, thời gian bắt đầu và dừng có thể không nằm trong nhật ký." Không, đọc lại OP. OP nói rằng những người đó sẽ có mặt nhưng các dòng can thiệp sẽ không nhất thiết phải bắt đầu bằng dấu thời gian. Nó thậm chí không có ý nghĩa để nói rằng thời gian dừng có thể không có mặt. Làm thế nào bạn có thể nói cho bất kỳ công cụ nào dừng lại nếu dấu chấm dứt không được đảm bảo ở đó? Sẽ không có tiêu chí để cung cấp cho công cụ để cho nó biết nơi dừng xử lý.
Bratchley
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.