1

(edit: Problem solved, I used windows-generated files on a mac and the problem was the line endings. opening the file in studio code and setting proper line endings solved this for me)

I have files generated by ProduKey that I want to process with awk.

ProduKey produces output like this:

==================================================
Product Name      : Microsoft Office Professional 2013
Product ID        : 00000-00000-00000-00000
Product Key       : 00000-00000-00000-00000-00000
Installation Folder : C:\Program Files\Microsoft Office\Office15\
Service Pack      : 
Build Number      : 
Computer Name     : PC-000-0
Modified Time     : 06.12.2018 14:03:44
==================================================

==================================================
Product Name      : Windows 10 Pro
Product ID        : 00000-00000-00000-00000
Product Key       : 00000-00000-00000-00000-00000
Installation Folder : C:\Windows
Service Pack      : 
Build Number      : 17763
Computer Name     : PC-000-0
Modified Time     : 18.07.2019 09:50:37
==================================================

I want to generate csv files from that to store key, computer name and product name to a database later.

so a possible csv could look like this:

PC-000-0;Microsoft Office Professional 2013;00000-00000-00000-00000-00000

My approach so far:

    BEGIN {
    RS="\n\n";
    FS="\n";
}
{
    if ($1 ~ /Product Name/) {
        split($1,productArray,":")
        product = productArray[2]
    }

    if ($1 ~ /Product Key/) {
        split($1,keyArray,":")
        key = keyArray[2]
    }

    if ($1 ~ /Computer Name/ ) {
        split($1,computerArray,":")
        computer = computerArray[2]
        #print product 
        #print key
        #print computer
        printf("\n")
        printf("%s ; %s ; %s \n", computer, product, key)
    }
}

My problem is that I cannot concat the strings. Or maybe the strings are overriden by the moment i want to print them? I spend hours on this and would really appreciate your help/hints.

If there are more elegant ways to reach my goal feel free to tell me.

I am using awk on a Mac (awk version 20070501) but I have a Linux box as well.

Thanks in advance.

1

You can play with awk's record separator (RS) here. Setting it to ================================================== or ={50} which is the same thing but as a regex, lets you treat each set of values associated with a single product as a single "line". If you then set the output field separator (OFS) to ; and the input field separator (FS, or the -F option) to \n (newline) or :, that lets you do little tricks like this (using GNU awk):

$ awk RS='={50}' \
      -v OFS=';' \
      -F'[\n:]' \
      'NR%2==0{print $16,$3,$7}' file 
 PC-000-0; Microsoft Office Professional 2013; 00000-00000-00000-00000-00000
 PC-000-0; Windows 10 Pro; 00000-00000-00000-00000-00000

Since each entry has a ============================================= (50 =) above and one below, we only want to read every second "line". That's what the NR%2==0 check does.

There are some extra spaces there, one at the start of each ine and another between the fields. If they're a problem, you can remove them with sed:

$ awk -v RS='={50}' \
      -v OFS=';' \
      -F '[\n:]' \
      'NR%2==0{print $16,$3,$7}' file | 
        sed -e 's/^ //' -e 's/; /;/g'
PC-000-0;Microsoft Office Professional 2013;00000-00000-00000-00000-00000
PC-000-0;Windows 10 Pro;00000-00000-00000-00000-00000

The use of a regular expression for the RS is a GNU extension, so will probably not work on your macOS. If not, try this instead:

$ awk -v RS='=' \
      -v OFS=';' \
      -F'[\n:]' \
     'NR%50==1 && $2{print $16,$3,$7}' file |
        sed -e 's/^ //' -e 's/; /;/g'
| improve this answer | |
  • If I use your snippet it only prints out a lot of ;; I will try on a Linux box later, maybe this does not work with awk that is preinstalled on a mac. – tux Aug 20 '19 at 12:15
  • @tux yes, it will work on Linux, but will print the ; on non-GNU awk. But try the updated one. That should work with any awk implementation. – terdon Aug 20 '19 at 12:18
  • Wow, the updated version works like a charm! I can only stare at it but I don't really understand how it works - I will try to figure that out and create a script. I want to use it on dozens of files. Thank you @terdon – tux Aug 20 '19 at 12:27
  • @tux it probably won't work, actually. based on your comment here, you have a file with windows line endings. So first remove those with sed 's/\r//' file > file.fixed. The trick I'm using is the NR%50==1. That's because there are 50 =, and each of them defines a "line". Therefore, I want the 51st and then the 151st etc. In other words, all line numbers which return 1 modulo 50. – terdon Aug 20 '19 at 12:30
1

You could try the following:

awk -F' : ' '/^Product Name/{pn=$2}
             /^Product Key/{pk=$2}
             /^Computer Name/{print $2,pn,pk}' OFS=\; file

The field separator FS is set to : in order to get the field value with $2.

The output field separator OFS is set to ;.

The string is printed everytime the keyword Computer Name is found.

| improve this answer | |
  • i tried to use your snippet in my script, my script looks now like this: BEGIN { RS="\n\n"; FS=" : "; OFS="\;"; } { if ($1 ~ /^Product Name/) { pn=$2 } if ($1 ~ /^Product Key/) { pk=$2 } if ($1 ~ /^Computer Name/) { print $2, pn, pk }; } but output is still gibberish, ;00000-00000-00000-00000-00000 2013 ;00000-00000-00000-00000-00000 What am I doing wrong? – tux Aug 20 '19 at 11:48
  • @tux Please use the script as is. – oliv Aug 20 '19 at 12:03
  • as far as I understand your snippet cannot be used as script but for usage from command line only. If I am misunderstanding your snippet please let me know what the script should look like. – tux Aug 20 '19 at 12:11
  • @tux the script from your comment should also work. Is there something strange with your file? Could it have been generated on a Windows machine perhaps or a very old mac? Try running head -n1 file | sed 's/.*\(.\)/\1/' | od -c, does that show you a single \n at the end or is there a \r there? I just tested and get the output you describe if the input file has Windows line endings. – terdon Aug 20 '19 at 12:23
  • yes, you are right, ProduKey is a Windows App, all the files were generated on Windows machines. My output is 0000000 \r \n 0000002 so it is a problem with line endings i suppose, i should have known it. Thank you for pointing out, i completely forgot. – tux Aug 20 '19 at 12:34
0

The right way to do this is to create an array that stores the tag name to value mappings (f[] below) and then just print whatever values you want at the end of every record, e.g.:

$ cat tst.awk
BEGIN { OFS=";" }
{
    sub(/\r$/,"")
    tag = val = $0
    sub(/[[:space:]]*:.*/,"",tag)
    sub(/[^:]+[[:space:]]*:[[:space:]]*/,"",val)
    f[tag] = val
}
/^=/ && (cnt++)%2 {
    print f["Computer Name"], f["Product Name"], f["Product Key"]
    delete f
}

$ awk -f tst.awk file
PC-000-0;Microsoft Office Professional 2013;00000-00000-00000-00000-00000
PC-000-0;Windows 10 Pro;00000-00000-00000-00000-00000

With this same approach it's easy to convert your whole input file to a CSV too if you like:

$ cat tst.awk
BEGIN { OFS=";" }
!/^=/ {
    sub(/\r$/,"")
    tag = val = $0
    sub(/[[:space:]]*:.*/,"",tag)
    sub(/[^:]+[[:space:]]*:[[:space:]]*/,"",val)
    if ( !seen[tag]++ ) {
        tags[++numTags] = tag
    }
    f[tag] = val
}
/^=/ && (cnt++)%2 {
    if ( !doneHdr++ ) {
        for (tagNr=1; tagNr<=numTags; tagNr++) {
            tag = tags[tagNr]
            printf "\"%s\"%s", tag, (tagNr<numTags ? OFS : ORS)
        }
    }

    for (tagNr=1; tagNr<=numTags; tagNr++) {
        tag = tags[tagNr]
        printf "\"%s\"%s", f[tag], (tagNr<numTags ? OFS : ORS)
    }

    delete f
}

$ awk -f tst.awk file
"Product Name";"Product ID";"Product Key";"Installation Folder";"Service Pack";"Build Number";"Computer Name";"Modified Time"
"Microsoft Office Professional 2013";"00000-00000-00000-00000";"00000-00000-00000-00000-00000";"C:\Program Files\Microsoft Office\Office15\";"";"";"PC-000-0";"06.12.2018 14:03:44"
"Windows 10 Pro";"00000-00000-00000-00000";"00000-00000-00000-00000-00000";"C:\Windows";"";"17763";"PC-000-0";"18.07.2019 09:50:37";""

Change OFS=";" to OFS="," and the output will be a file you can double-click on in Windows to have it displayed correctly in Excel.

| improve this answer | |

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.