İki zaman damgası arasında günlükler nasıl çıkarılır?


25

İki zaman damgası arasındaki bütün kayıtları çıkartmak istiyorum. Bazı satırlarda zaman damgası olmayabilir, ancak bu satırları da istiyorum. Kısacası, iki zaman damgasının altına düşen her çizgiyi istiyorum. Günlük yapım şuna benziyor:

[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

Diyelim ki 2014-04-07 23:00ve arasındaki her şeyi çıkarmak istiyorum 2014-04-08 02:00.

Lütfen başlangıç ​​zaman damgasının veya bitiş zaman damgasının kayıt defterinde bulunmadığını unutmayın, ancak bu iki zaman damgası arasındaki her çizgiyi istiyorum.



Bunu sadece bir kere mi yoksa programsal olarak muhtelif zamanlarda mı yapmanız gerekiyor?
Bratchley

Benim sormamın sebebi , değişmez değerleri biliyorsanız iki bağlamsal grep'i (biri başlangıç ​​sınırlayıcısından sonra her şeyi almak, diğeri sınırlayıcıda yazdırmayı durdurmak için) yapabilmenizdir. Tarihler / saatler değişebiliyorsa, tou bu date -dkomutları anında kullanıcı girişi komutunu kullanarak besleyerek ve bunu arama şablonunu oluşturmak için kullanarak kolayca oluşturabilir .
Bratchley

@Ramesh, başvurulan soru çok geniş.
maxschlepzig

@JoelDavis: Programlı olarak yapmak istiyorum. Bu yüzden her zaman / tmp konumlarımdaki zaman damgası arasındaki günlükleri çıkarmak için sadece istenen zaman damgasını girmem gerekiyor.
Amit

Yanıtlar:


19

Bunun için kullanabilirsiniz 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

Nerede:

  • -FNormal bir ifade kullanarak karakterleri [ve ]alan ayırıcıları belirtir.
  • $0 tam bir satıra atıfta bulunur
  • $2 tarih alanını referans alıyor
  • p Gerçek yazdırmayı koruyan Boolean değişkeni olarak kullanılır.
  • $0 ~ /regex/ regex eşleşirse doğrudur $0
  • >=sözlükleri karşılaştırmak için kullanılır (eg'ye eşdeğer strcmp())

Varyasyonlar

Yukarıdaki komut satırı, sağ-açık zaman aralığı eşleştirmesini uygular . Kapalı aralıklı anlambilim elde etmek için doğru tarihinizi arttırın, örneğin:

$ 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

Zaman damgalarını başka bir formatta eşleştirmek isterseniz, $0 ~ /^\[/alt ifadeyi değiştirmeniz gerekir . Yazdırma açık / kapalı mantığından herhangi bir zaman damgası olmadan satırları yoksaymak için kullanıldığını unutmayın.

Örneğin, YYYY-MM-DD HH24:MI:SS( []ayraçsız) gibi bir zaman damgası biçimi için , komutu şöyle değiştirebilirsiniz:

$ 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

(alan ayırıcısının da değiştirildiğine dikkat edin - varsayılan, boş / boş olmayan geçiş olarak)


Komut dosyasını paylaştığınız için teşekkür ederiz ancak bitiş zaman damgasını kontrol etmeyin. Kontrol edebilir misiniz? Ayrıca bana günlükleri varsa 2014-04-07 23:59:58 gibi ne olduğunu bildir. Diş telleri olmadan demek istiyorum
Amit

@Amit, cevabı güncelledi
maxschlepzig

Bunun bir sicim problemi olduğunu düşünmeme rağmen ( cevabımı görün ), tüm testleri tekrarlayarak sizinkini daha okunaklı ve muhtemelen biraz daha hızlı hale getirebilirsiniz:$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

Merhaba Max, Bir küçük şüphe .. Eğer Nis-07-2014 10:51:17 gibi bir şey varsa. Sonra ne yapmam gerekecek .. code$ 0 ~ / ^ [az | AZ] {4} - [0-9] {2} - [0-9] {4} [0-2] [0-9 denedim. ]: [0-5] [0-9]: [0-5] [0-9] / &&1 $ "" $ 2> = "Nis-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> = "Apr-07-2014 00:00:01"{p = 0} code, ancak çalışmıyor onun
Amit

@ awk_FTW, kodu, regex'in açıkça paylaşıldığı şekilde değiştirdi.
maxschlepzig

12

Https://github.com/mdom/dategrep adresinden göz dategrepatın

Açıklama:

dategrep, adlandırılmış giriş dosyalarını bir tarih aralığına uyan çizgiler için arar ve stdout'a yazdırır.

Dategrep aranabilir bir dosyada çalışıyorsa, oldukça verimli bir şekilde yazdırmak için ilk ve son satırı bulmak için ikili arama yapabilir. dategrep, dosya ismi argümanlarından sadece biri kısa çizgi ise stdin'den de okuyabilir, ancak bu durumda daha yavaş olacak her satırı ayrıştıracaktır.

Kullanım örnekleri:

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

Bu sınırlama bunu kesin sorunuz için uygun hale getiremese de:

Dategrep şu anda, ayrılmaz bir çizgi bulduğu anda ölecek. Gelecekteki bir sürümde bu yapılandırılabilir.


Bu emri sadece birkaç gün önce onethingwell.org/post/81991115668/dategrep izniyle öğrendim .
cpugeniusmv

3

awkStandart olmayan bir araca alternatif veya alternatif bir araç, GNU’yu grepiçeriksel izleri için kullanmaktır . GNU'lar grep, yazdırılacak pozitif eşleşmeden sonraki satır sayısını ve yazdırılacak -Aönceki satırları belirtmenize izin verir. -BÖrneğin:

[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

Yukarıdakiler esas olarak, grepbaşlamak istediğiniz kalıba uygun çizgiyi takip eden 10.000 çizgiyi yazdırmayı, çıktılarınızı istediğiniz yere başlamasını ve sonuna kadar (umarım) ikinciyi geçmesini sağlar egrep. boru hattı, yalnızca son sınırlayıcı ve ondan önceki 10.000 satır ile satırı yazdırmasını söyler. Bu ikisinin sonucu, istediğin yerden başlayıp, gitmemek için durmanı söylediğin yerden geçmektir.

Çıktığımın çok uzun olacağını düşünüyorsanız, 10.000 sadece geldiğim bir sayı, milyon olarak değiştirmekten çekinmeyin.


Başlangıç ​​ve bitiş aralıkları için bir günlük girişi yoksa, bu nasıl çalışır? OP 14: 00-15: 00 saatleri arasında her şeyi istiyorsa, ancak 14: 00 için kayıt girişi yok, o zaman?

Bunun yanı sıra sed, aynı zamanda kelimenin tam anlamıyla eşleşmeleri de araştıracağı hakkında söz verecektir . dategrepMuhtemelen verilenlerin en doğru cevabıdır (kabul edeceğiniz zaman diliminde "belirsiz" olması gerektiğine rağmen) ancak cevabın dediği gibi, alternatif olarak sadece bahsettiğim gibi. Bu, eğer kütük kesme işlemine yetecek kadar çıktı üretmek için kütüğün yeterince aktif olması durumunda , verilen zaman dilimine muhtemelen bir tür giriş yapacağı da söylenebilir .
Bratchley

0

Sed kullanarak:

#!/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"

Bunu bir dosyaya kopyalayın. Hata ayıklama bilgisini görmek istemiyorsanız, hata ayıklama stderr'e gönderilir, bu nedenle sadece "2> / dev / null" ekleyin.


1
Bu zaman damgası olmayan günlük dosyalarını görüntülemez.
Amit

@Amit, evet, denediniz mi?
UnX

@ rMistero, çalışmayacak çünkü 22: 30'da günlük girişi yoksa, aralık sonlandırılmayacaktır. OP'nin dediği gibi, başlama ve durma zamanları günlüklerde olmayabilir. Regex'inizin çalışması için ince ayar yapabilirsiniz, ancak çözünürlüğü kaybedersiniz ve aralığın doğru zamanda sona ereceği konusunda hiçbir zaman önceden bir garanti vermezsiniz .

@awk_FTW bu bir örnekti, Amit tarafından sağlanan zaman damgalarını kullanmadım. Yine regex kullanılabilir. Açıkça belirtildiğinde veya zaman damgası regex eşleşmesi olmadığında zaman damgası yoksa, işe yaramayacağını düşünüyorum. Yakında geliştireceğim ..
UnX

"OP'nin dediği gibi, başlangıç ​​ve bitiş zamanları kayıt defterinde olmayabilir." Hayır, OP'yi tekrar okuyun. OP, bu WILL'lerin bulunacağını, ancak araya giren çizgilerin mutlaka bir zaman damgasıyla başlamayacağını söylüyor. Durma zamanlarının olamayacağını söylemek bile mantıklı gelmiyor. Herhangi bir araca, sonlandırma işaretinin orada olması garanti edilmediğinde nerede durması gerektiğini nasıl söyleyebilirsiniz ? Aracı, işlemeyi nerede durduracağını söylemek için verebilecek kriter yoktur.
Bratchley
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.