#!/usr/bin/gawk -f BEGIN { progvers = "2008-10-11a" } # # datestamp-filter -- 2006-03-05, last edit -> above # # Filter log file by datestamp in standard form: "Aug 4 17:53:23", with # optional preset column offset to datestamp start, removes blank records # and records with invalid dates, outputs records matching date criteria. # Defaults to printing records from most recent 24 hours. # # Includes a debug option that displays the datestamp start cursor with # parsed datestamp value and good/bad status indicator. # # Copyright (C) 2006-2008 Grant Coady GPLv2 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # 2008-10-11 # add automagical timestamp offset finder, remove debug option, etc needed # for the old specify date start column method # #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # datestamp filter manual # ```````````````````````` # not specifying a value sets it to zero, or the default value indicated # below. CLI options with -v prefix must appear before non-prefixed # options, and all options must appear before data filenames. # # You may also pipe data or log files into this program, for example: # # ~$ last| tac| datestamp-filter -v hours=36 datecolm=44 # # This program is written for GNU awk, tested on gawk v.3.1.5 and 3.1.6 # # start and finish are specified in epoch seconds, use the date function # to perform the conversion from 'human readable' time into epoch seconds, # for example: -v start=$(date -d "2006-03-06 12:00" +%s) # # options: 1) x hours before finish if start == 0, hours > 0 # 2) x hours after start if start > 0, hours > 0 # 3) start then finish if start > 0, start < finish # # an activity indicator (rec nr) is output on stderr while skipping records # #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # option description default # ------------ ---------------------------------------------- -------- # -v hours=n number of hours after start or before finish # to perform logfile parsing, see above 24 # # -v start=n start time in epoch seconds, see above 0 # # -v finish=n finish time in epoch seconds, see above 0 # # -v year log start year, default is this year, # switching to last year if first log # record timestamp is in the future # # -v verbose=1 print version and start / finish times 0 # # -v defsec=n number of seconds to use with empty seconds # field, defaults to 0, or > 59 defaults to 59 0 or 59 BEGIN { progname = "datestamp-filter" proghome = "website: http://bugsplatter.id.au/awk/" now = systime() if (verbose) { printf "\n%s -- %s, %s\n", progname, progvers, proghome } # create a lookup table to convert short month name to 1 - 12 n = split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", a) for (i = 1; i <= n; i++) { month[a[i]] = i } # interpret CLI options to get start / finish times hours = (hours < 1 ? 24 : hours) # default 24 hour finish = (finish ? finish : now) # default to now! if (!start) { start = finish - hours * 3600 } else { finish = start + hours * 3600 } if (year < 1970 || year > 2037) { year = strftime("%Y", systime()) } # sanity check empty seconds field replacement value if (defsec > 59) { defsec = 59 } if (defsec < 1) { defsec = 0 } } # skip blank lines /^$/ { next } # discover record timestamp { ++count # locate start of date string for (i = 1; i <= NF; i++) { if ($i~/^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)$/){ break } } if (i >= NF) { next } # skip if no date start found # extract standard form datestamp: Aug 4 09:37:23 mon = month[$i]; day = $++i split($++i, a, ":") a[3] = (a[3] ? a[3] : defsec) year += (mon < last_mon ? 1 : 0) last_mon = mon # convert the logfile record's datestamp to unix epoch time recd_ds = mktime(year" "mon" "day" "a[1]" "a[2]" "a[3]) } # no further processing if bad date decode (recd_ds < 1) && !bad_date { bad_date = count } (recd_ds < 1) { next } # set log to last year if log start is in the future (!log_start) { log_start = recd_ds if (log_start > now) { year = strftime("%Y", now) - 1 log_start = mktime(year" "mon" "day" "a[1]" "a[2]" "a[3]) } } # activity indicator to stderr count % 567 == 0 && (recd_ds < start || recd_ds > finish) { printf "\r%8d\r", count > "/dev/stderr" } # skip unwanted record (recd_ds < start || recd_ds > finish) { next } # print wanted record { found++; print } END { if (!found || verbose) { printf "### %s: %d matching records from %d\n", progname, found, count printf "### filtered: %s to %s\n", strftime("%Y %b %d %H:%M:%S", start), strftime("%Y %b %d %H:%M:%S (%z)", finish) printf "### logfile: %s to %s\n", strftime("%Y %b %d %H:%M:%S", log_start), strftime("%Y %b %d %H:%M:%S (%z)", recd_ds) } if (bad_date) { printf "### %s: bad found date on line %d (first seen)\n", progname, bad_date } } # end