Any awk gurus on the list?

Wayne Sierke ws at au.dyndns.ws
Sun Aug 22 16:45:37 UTC 2010


On Fri, 2010-08-20 at 12:12 -0500, Paul Schmehl wrote:
> I'm trying to figure out how to use awk to parse values from a string of 
> unknown length and unknown fields using awk, from within a shell script, and 
> write those values to a file in a certain order.
> 
> Here's a typical string that I want to parse:
> 
> alert ip 
> [50.0.0.0/8,100.0.0.0/6,104.0.0.0/5,112.0.0.0/6,173.0.0.0/8,174.0.0.0/7,176.0.0.0/5,184.0.0.0/6] 
> any -> $HOME_NET any (msg:"ET POLICY Reserved IP Space Traffic - Bogon Nets 2"; 
> classtype:bad-unknown; reference:url,www.cymru.com/Documents/bogon-list.html; 
> threshold: type limit, track by_src, count 1, seconds 360; sid:2002750; rev:10;)
> 
> What I want to do is extract the value after "sid:", the value after 
> "reference:" and the value after "msg:" and insert them into a file that would 
> look like this:
> 
> 2002750 || "ET POLICY Reserved IP Space Traffic - Bogon Nets 2" || 
> url,www.cymru.com/Documents/bogon-list.html

Probably not a complete solution for your problem domain but you might
glean an idea or two from this:

        awk 'BEGIN {FS="\\(|; *"} /#/ {next} {for(i=1;i<=NF;i++) print
        $i}' mtc.rules.test | awk 'BEGIN {FS=":" ; OFS=" || "} $1 ==
        "sid" {sid=$2} $1 == "msg" {msg=$2} $1 == "reference" {ref=$2}
        $1 == ")" {print sid,msg,ref}'

> Yes, I know I could do this easily in Perl.  I'm doing this to try and improve 
> my understanding of awk.  I *think* I've figured out that the right approach is 
> to use an associative array, and this command:

No need for an array unless you want to retain the records for later
processing. For simple record-by-record processing scalar vars suffice.

> #  awk '!/#/ { for (i=1; i<=NF; i++) { if ( $i ~ /sid/) {mtcmsg[sid]=$i; print 
> mtcmsg[sid]}}}' < /usr/local/etc/snort/rules/mtc.rules.test

A couple of things to note:

        $i ~ /sid/ : will match the string "sid" anywhere within the
        field - either use ~ /^sid$/  or  == "sid" for exact matching
        mtcmsg[sid] : references the scalar var named "sid" (which is
        empty => mtcmsg[""])


Of course, if you choose to you can also just execute some
run-of-the-mill regex matches and string manipulations in awk:

        awk '!/#/ {s1=match($0, "sid:[^;]*"); if (s1) sid=substr($0,
        RSTART+4, RLENGTH-4); s2=match($0, "msg:[^;]*"); if (s2)
        msg=substr($0, RSTART+4, RLENGTH-4); s3=match($0,
        "reference:[^;]*"); if (s3) ref=substr($0, RSTART+10,
        RLENGTH-10); if (s1*s2*s3) print sid" || "msg" || "ref}'
        mtc.rules.test

but that lacks any real awk-ness.


Wayne




More information about the freebsd-questions mailing list