วิธีแยกล็อกระหว่างการประทับเวลาสองครั้ง


25

ฉันต้องการแยกบันทึกทั้งหมดระหว่างการประทับเวลาสองครั้ง บางบรรทัดอาจไม่มีการประทับเวลา แต่ฉันต้องการเส้นเหล่านั้นด้วย ในระยะสั้นฉันต้องการทุกบรรทัดที่อยู่ภายใต้การประทับเวลาสองครั้ง โครงสร้างบันทึกของฉันดูเหมือนว่า:

[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

สมมติว่าผมต้องการที่จะดึงทุกอย่างระหว่างและ2014-04-07 23:002014-04-08 02:00

โปรดทราบว่าการประทับเวลาเริ่มต้นหรือการประทับเวลาสิ้นสุดอาจไม่มีอยู่ในบันทึก แต่ฉันต้องการทุกบรรทัดระหว่างการประทับเวลาสองครั้งนี้


เป็นไปได้ซ้ำของstackoverflow.com/questions/7575267/…
Ramesh

คุณเพียงแค่ต้องทำสิ่งนี้เพียงครั้งเดียวหรือเป็นโปรแกรมในช่วงเวลาต่าง ๆ ?
Bratchley

เหตุผลที่ฉันถามคือเพราะคุณสามารถทำgrep ตามบริบทได้สองรายการ (อันหนึ่งจะคว้าทุกอย่างหลังจากตัวคั่นเริ่มต้นและอีกตัวเพื่อหยุดการพิมพ์ที่ตัวคั่นสิ้นสุด) หากคุณรู้ค่าตัวอักษร หากวันที่ / เวลาสามารถเปลี่ยนแปลงได้คุณสามารถสร้างสิ่งเหล่านี้ได้ทันทีโดยป้อนข้อมูลผู้ใช้ผ่านdate -dคำสั่งและใช้สิ่งนั้นเพื่อสร้างรูปแบบการค้นหา
Bratchley

@Ramesh คำถามที่อ้างอิงนั้นกว้างเกินไป
maxschlepzig

@ JoelDavis: ฉันต้องการที่จะทำมันเป็นโปรแกรม ดังนั้นทุกครั้งที่ฉันต้องป้อนการประทับเวลาที่ต้องการเพื่อแยกบันทึกระหว่างการประทับเวลาเหล่านั้นในตำแหน่ง / tmp ของฉัน
Amit

คำตอบ:


19

คุณสามารถใช้awkสิ่งนี้:

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

ที่ไหน:

  • -Fระบุอักขระ[และ]เป็นตัวคั่นฟิลด์โดยใช้นิพจน์ทั่วไป
  • $0 อ้างอิงบรรทัดที่สมบูรณ์
  • $2 อ้างอิงฟิลด์วันที่
  • p ใช้เป็นตัวแปรบูลีนที่ป้องกันการพิมพ์จริง
  • $0 ~ /regex/ เป็นจริงถ้า regex ตรงกัน $0
  • >=ใช้สำหรับการเปรียบเทียบสตริงพจนานุกรม (เทียบเท่าเช่นstrcmp())

รูปแบบ

บรรทัดคำสั่งด้านบนใช้การจับคู่ช่วงเวลาที่เปิดขวา ในการรับซีแมนติกแบบปิดช่วงเวลาเพียงเพิ่มวันที่ที่ถูกต้องของคุณเช่น:

$ 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

ในกรณีที่คุณต้องการจับคู่การประทับเวลาในรูปแบบอื่นคุณต้องแก้ไข$0 ~ /^\[/นิพจน์ย่อย โปรดทราบว่ามันใช้เพื่อละเว้นบรรทัดโดยไม่มีการประทับเวลาใด ๆ จากการพิมพ์เปิด / ปิดตรรกะ

ตัวอย่างเช่นสำหรับรูปแบบการประทับเวลาเช่นYYYY-MM-DD HH24:MI:SS(ไม่มี[]วงเล็บปีกกา) คุณสามารถแก้ไขคำสั่งดังนี้:

$ 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

(โปรดทราบว่าตัวคั่นฟิลด์ถูกเปลี่ยน - เป็นการเปลี่ยนว่าง / ไม่ว่างค่าเริ่มต้น)


ขอบคุณสำหรับการแบ่งปันสคริปต์ แต่มันไม่ได้ตรวจสอบการประทับเวลาสิ้นสุด .. คุณช่วยกรุณาตรวจสอบ นอกจากนี้ให้ฉันรู้ว่าถ้าฉันมีบันทึกเช่น 2014-04-07 23:59:58 ฉันหมายถึงไม่มีวงเล็บปีกกา
Amit

@ ยอมรับอัปเดตคำตอบ
maxschlepzig

แม้ว่าฉันไม่คิดว่านี่เป็นปัญหาสตริง (ดูคำตอบของฉัน ) คุณสามารถทำให้คุณอ่านได้มากขึ้นและอาจเร็วขึ้นเล็กน้อยโดยไม่ทำแบบทดสอบซ้ำทั้งหมด: $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

สวัสดีแม็กซ์อีกหนึ่งข้อสงสัยเล็ก ๆ น้อย ๆ .. ถ้าฉันมีบางอย่างเช่น เม.ย. -7-2557 10:51:17 ฉันต้องลองทำ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> = "เม.ย. -7-2557 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> = "Apr-07-2014 12:00:01" {p = 0} codeแต่มันไม่ทำงาน
Amit

@awk_FTW เปลี่ยนรหัสเพื่อให้ regex มีการแชร์อย่างชัดเจน
maxschlepzig

12

ตรวจสอบdategrepที่https://github.com/mdom/dategrep

รายละเอียด:

dategrep ค้นหาไฟล์อินพุตที่ระบุชื่อเพื่อหาบรรทัดที่ตรงกับช่วงวันที่และพิมพ์ไปยัง stdout

หาก dategrep ทำงานกับไฟล์ที่ค้นหาได้มันสามารถทำการค้นหาแบบไบนารีเพื่อค้นหาบรรทัดแรกและบรรทัดสุดท้ายเพื่อพิมพ์ได้อย่างมีประสิทธิภาพ dategrep ยังสามารถอ่านได้จาก stdin หากหนึ่งชื่อไฟล์อาร์กิวเมนต์เป็นเพียงเครื่องหมายยัติภังค์ แต่ในกรณีนี้มันจะต้องแยกวิเคราะห์ทุกบรรทัดเดียวซึ่งจะช้าลง

ตัวอย่างการใช้งาน:

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" -

แม้ว่าข้อ จำกัด นี้อาจทำให้คำถามนี้ไม่เหมาะสมกับคุณ:

ในขณะนี้ dategrep จะตายทันทีที่พบบรรทัดที่ไม่สามารถแยกวิเคราะห์ได้ ในรุ่นอนาคตนี้จะสามารถกำหนดค่าได้


ผมได้เรียนรู้เกี่ยวกับคำสั่งนี้เพียงไม่กี่วันที่ผ่านมามารยาทของonethingwell.org/post/81991115668/dategrepเพื่อความรุ่งโรจน์ให้กับเขา!
cpugeniusmv

3

ทางเลือกหนึ่งawkหรือเครื่องมือที่ไม่ได้มาตรฐานคือการใช้ GNU grepสำหรับ greps เชิงบริบท GNU's grepจะให้คุณระบุจำนวนบรรทัดหลังจากการจับคู่แบบบวกเพื่อพิมพ์ด้วย-Aและบรรทัดก่อนหน้าเพื่อพิมพ์ด้วย-Bตัวอย่างเช่น:

[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

ข้างต้นเป็นหลักบอกgrepให้พิมพ์ 10,000 บรรทัดที่เป็นไปตามบรรทัดที่ตรงกับรูปแบบที่คุณต้องการที่จะเริ่มต้นได้อย่างมีประสิทธิภาพทำให้การส่งออกของคุณเริ่มต้นที่คุณต้องการและไปจนถึงจุดสิ้นสุด (หวังว่า) ในขณะที่สองegrepใน ไปป์ไลน์บอกให้พิมพ์บรรทัดด้วยตัวคั่นสิ้นสุดและ 10,000 บรรทัดก่อนหน้าเท่านั้น ผลลัพธ์สุดท้ายของสองสิ่งนี้เริ่มต้นที่คุณต้องการและไม่ผ่านที่คุณบอกให้หยุด

10,000 เป็นเพียงตัวเลขที่ฉันคิดขึ้นมาอย่าลังเลที่จะเปลี่ยนเป็นล้านถ้าคุณคิดว่าผลลัพธ์ของคุณจะยาวเกินไป


สิ่งนี้จะทำงานได้อย่างไรหากไม่มีรายการบันทึกสำหรับช่วงเริ่มต้นและสิ้นสุด หาก OP ต้องการทุกอย่างระหว่าง 14:00 น. ถึง 15:00 น. แต่ไม่มีรายการบันทึกเป็นเวลา 14:00 น.

มันจะเป็นคำเกี่ยวกับเช่นเดียวกับsedที่ยังค้นหาการแข่งขันที่แท้จริง dategrepอาจเป็นคำตอบที่ถูกต้องที่สุดของคำตอบทั้งหมด (เนื่องจากคุณจำเป็นต้องได้รับ "fuzzy" ในสิ่งที่คุณยอมรับ timestamps) แต่เหมือนคำตอบบอกว่าฉันแค่พูดถึงมันเป็นทางเลือก ที่กล่าวว่าหากบันทึกมีการใช้งานมากพอที่จะสร้างผลผลิตเพียงพอที่จะรับประกันการตัดมันอาจเป็นไปได้ที่จะมีรายการบางอย่างสำหรับช่วงเวลาที่กำหนด
Bratchley

0

ใช้ 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"

คัดลอกสิ่งนี้ในไฟล์ หากคุณไม่ต้องการเห็นข้อมูลการดีบักการดีบักจะถูกส่งไปยัง stderr ดังนั้นเพียงเพิ่ม "2> / dev / null"


1
นี่จะไม่แสดงไฟล์บันทึกที่ไม่มีการประทับเวลา
Amit

@ ยอมรับใช่มันจะลองคุณ?
UnX

@rMistero มันจะไม่ทำงานเพราะถ้าไม่มีรายการบันทึกเวลา 22:30 ช่วงจะไม่ถูกยกเลิก ดังที่ OP กล่าวไว้เวลาเริ่มต้นและหยุดอาจไม่อยู่ในบันทึก คุณสามารถปรับแต่ง regex ของคุณเพื่อให้ใช้งานได้ แต่คุณจะคลายความละเอียดและไม่รับประกันล่วงหน้าว่าช่วงจะสิ้นสุดในเวลาที่เหมาะสม

@awk_FTW นี่เป็นตัวอย่างฉันไม่ได้ใช้การประทับเวลาที่ Amit จัดหาให้ สามารถใช้ regex อีกครั้งได้ ฉันเห็นด้วยคิดว่ามันจะไม่ทำงานหากไม่มีการประทับเวลาเมื่อให้อย่างชัดเจนหรือไม่มีการจับคู่ regex การประทับเวลา ฉันจะปรับปรุงให้ดีขึ้นเร็ว ๆ นี้ ..
Unx

"ดังที่ OP กล่าวถึงเวลาเริ่มต้นและหยุดอาจไม่อยู่ในล็อก" ไม่อ่าน OP อีกครั้ง OP กล่าวว่าสิ่งเหล่านั้นจะมีอยู่ แต่เส้นที่แทรกเข้ามานั้นไม่จำเป็นต้องเริ่มต้นด้วยการประทับเวลา มันไม่สมเหตุสมผลที่จะบอกว่าเวลาหยุดอาจไม่ปรากฏ คุณสามารถวิธีการที่เคยบอกใด ๆเครื่องมือที่จะหยุดถ้าเครื่องหมายการเลิกจ้างที่ไม่ได้รับประกันว่าจะมี? จะไม่มีเกณฑ์ให้เครื่องมือบอกตำแหน่งที่จะหยุดการประมวลผล
Bratchley
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.