5

Assuming that I have this file:

 Thu May  8 15:32:07 2014
        User-Name = "Mark"
        Framed-IP-Address = 0.0.0.0
        Acct-Status-Type = Interim-Update
        Acct-Input-Octets = 95684
        Acct-Output-Octets = 23564

    Thu May  8 15:32:07 2014
        User-Name = "Mike"
        Framed-IP-Address = 0.0.0.0
        Acct-Status-Type = Interim-Update
        Acct-Input-Octets = 95684
        Acct-Output-Octets = 23564
    Thu May  8 15:32:07 2014
        User-Name = "Mike"
        Framed-IP-Address = 0.0.0.0
        Acct-Status-Type = Interim-Update
        Acct-Input-Octets = 95684
        Acct-Output-Octets = 23564

    Thu May  8 15:32:07 2014
        User-Name = "Mark"
        Framed-IP-Address = 0.0.0.0
        Acct-Status-Type = Interim-Update
        Acct-Input-Octets = 95684
        Acct-Output-Octets = 23564

I have managed to get the Information that are related to a user using grep with -A option:

grep -A4 "Mark" test
        User-Name = "Mark"
        Framed-IP-Address = 0.0.0.0
        Acct-Status-Type = Interim-Update
        Acct-Input-Octets = 95684
        Acct-Output-Octets = 23564
--
        User-Name = "Mark"
        Framed-IP-Address = 0.0.0.0
        Acct-Status-Type = Interim-Update
        Acct-Input-Octets = 95684
        Acct-Output-Octets = 23564

but my desired output is something like that:

User-Name = "Mark" 
            Acct-Input-Octets = 95684 95684
            Acct-Output-Octets = 23564 23564

as we notice I want to eliminate the first two lines after "Mark" Framed-IP-Address = 0.0.0.0, Acct-Status-Type = Interim-Update and put all the values of the same field in the same line?

as Warwick suggest,the first part of my question can be easily answered:

grep -A4 "Mark" test| grep -v Framed-IP-Address | grep -v Acct-Status-Type

Note this is an example, the file may contain a lot of section with User-Name ="Mark" and the desired output will be something like that:

User-Name = "Mark" 
                Acct-Input-Octets = val1 val2 val3 val4 .......
                Acct-Output-Octets = val1 val2 val3 val4 ........
  • The first part of your question would be fairly easily achieved by adding to your command | grep -v Framed-IP-Address | grep -v Acct-Status-Type. For the second part of your question, if you are familiar with Perl, then I'd suggest using objects or hashes of arrays, where the array elements are the values. – Warwick Aug 1 '14 at 1:12
  • Are you open to other solutions? grep isn't smart enough for this. – Cristian Ciupitu Aug 1 '14 at 1:55
  • any command will work for me @CristianCiupitu – Networker Aug 1 '14 at 1:56
  • Are the blank lines between datasets optional? – tink Aug 1 '14 at 2:27
  • @tink, no it is always exist – Networker Aug 1 '14 at 7:32
3

search.awk

BEGIN {
    FS = "="
    cur_username = ""
}

$1 ~ /User-Name/ {
    cur_username = $2
    gsub(/^[ \t]+/, "", cur_username)
    gsub(/[ \t]+$/, "", cur_username)
}

$1 !~ /User-Name/ {
    if ((NF != 2) || (cur_username != searched_user))
        next

    key = $1
    gsub(/^[ \t]+/, "", key)
    gsub(/[ \t]+$/, "", key)

    value = $2
    gsub(/^[ \t]+/, "", value)
        gsub(/[ \t]+$/, "", value)

    values[key] = values[key] " " value
}

END {
    printf("User-Name = %s\n", searched_user)
    for(key in values) {
        printf("\t%s =%s\n", key, values[key])
    }
}

Test run:

$ awk -f search.awk -v 'searched_user="Mark"' input 
User-Name = "Mark"
    Acct-Status-Type = Interim-Update Interim-Update
    Acct-Input-Octets = 95684 95684
    Framed-IP-Address = 0.0.0.0 0.0.0.0
    Acct-Output-Octets = 23564 23564

Bonus - group.awk for grouping all the records (too bad nawk doesn't have asorti):

BEGIN {
    FS = "="
    cur_username = ""
}

$1 ~ /User-Name/ {
    cur_username = $2
    gsub(/^[ \t]+/, "", cur_username)
    gsub(/[ \t]+$/, "", cur_username)
}

$1 !~ /User-Name/ {
    if (NF != 2)
        next

    key = $1
    gsub(/^[ \t]+/, "", key)
    gsub(/[ \t]+$/, "", key)

    value = $2
    gsub(/^[ \t]+/, "", value)
        gsub(/[ \t]+$/, "", value)

    users[cur_username,key] = users[cur_username,key] " " value
}

END {
    n = asorti(users, sorted)
    prev_username = ""
    for (i=1; i<=n; i++) {
        username_key = sorted[i]
        split(username_key, a, SUBSEP)
        username = a[1]
        key = a[2]
        value = users[sorted[i]]
        if (username != prev_username) {
            printf("User-Name = %s\n", username)
            prev_username = username
        }
        printf("\t%s =%s\n", key, value)
    }
}

Test run:

$ gawk -f group.awk input 
User-Name = "Mark"
    Acct-Input-Octets = 95684 95684
    ...
User-Name = "Mike"
    Acct-Input-Octets = 95684 95684
    ...
  • ,Thanks for your reply but this is didn't work its output is something like that:User-Name = "Mark", for Info I'm using centos6.5 – Networker Aug 1 '14 at 8:17
  • You're right, the same is happening to me with gawk-3.1.7-10.el6.i686, although it worked fine with gawk-4.1.0-3.fc20.x86_64. – Cristian Ciupitu Aug 1 '14 at 10:05
  • @Networker, it looks like that old gawk doesn't know about the \s regex. – Cristian Ciupitu Aug 1 '14 at 10:14
  • so I have to upgrade gawk – Networker Aug 1 '14 at 10:14
  • No, I've updated the answer in case you haven't noticed. It should work with gawk 3 too or even other Awk implementations, e.g. nawk. – Cristian Ciupitu Aug 1 '14 at 10:16
2

You could do something like

awk -vRS= -F'\n' '$2 ~ /Mark/ {
  vals["User-Name"] = "Mark"
  for (i=5;i<=NF;i++) {
    split($i,a," = ");
    vals[a[1]]=sprintf("%s %s", vals[a[1]], a[2]);
  }
}     
END{for (i in vals) print i,"=",vals[i];}' test

which for your test file gives

User-Name = Mark
        Acct-Input-Octets =  95684 95684
        Acct-Output-Octets =  23564 23564
  • The sprintf based concatenation could be replaced with vals[a[1]]=vals[a[1]] " " a[2]. – Cristian Ciupitu Aug 1 '14 at 2:56
  • @Steeldriver,Thanks for your reply but this will work only if we have user "mark"two times, as I said before the file may contain a lot of section with User-Name ="Mark". – Networker Aug 1 '14 at 7:35
  • It should work for any number of instances, provided the data blocks are separated by blank lines. I reduplicated your test input and confirmed it worked with 4 instances. – steeldriver Aug 1 '14 at 11:25
2
  1. Filter the desired paragraphs, to collect the data about this one user.
  2. Filter lines containing the desired keys (Acct-Input-Octets and Acct-Output-Octets). You may want to normalize spacing at this stage if it isn't consistent.
  3. Sort entries by key (using a stable sort unless you don't care about the order of the values).
  4. Collapse sequences of identical keys.
awk -v RS= '/User-Name = "Mark"/' |
grep -E 'Acct-(Input|Output)-Octets *=' |
sort -k1,1 -s |
awk '
  BEGIN {printf "User-Name = \"Mark\""}
  $1 == key { printf " %s", $3; }
  $1 != key { key = $1; printf "\n%s", $0; }
  END { print "" }'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.