Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash, Awk, Sed? Find string within file and append digits within string?

Tags:

bash

loops

sed

awk

I wish to search a large text file line by line, and find each entry containing "N:;;" and just simply change it to "N:07401000000;;" and then the next occurrence of "N:;;" would be changed to "N:07401000002;;" and so on throughout the complete file of entries. Here is an example below of before and after.

Before:

BEGIN:VCARD
VERSION:2.1
N:;;
TEL;TYPE=CELLVOICE:07401000000
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:;;
TEL;TYPE=CELLVOICE:07401000001
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:;;
TEL;TYPE=CELLVOICE:07401000002
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:;;
TEL;TYPE=CELLVOICE:07401000003
END:VCARD

After result would look like this:

BEGIN:VCARD
VERSION:2.1
N:07401000000;;
TEL;TYPE=CELLVOICE:07401000000
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:07401000001;;
TEL;TYPE=CELLVOICE:07401000001
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:07401000002;;
TEL;TYPE=CELLVOICE:07401000002
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:07401000003;;
TEL;TYPE=CELLVOICE:07401000003
END:VCARD

Any help or ideas would be awesome.

Do you want the N values to start at a hard-coded value and increment or just copy the value from the subsequent CELLVOICE?

Actually that is a good idea. How about the value mentioned within CELLVOICE.

like image 601
Darren Didge Avatar asked Sep 04 '26 19:09

Darren Didge


1 Answers

Here's the most robust and easily extensible way to do what you want:

$ cat tst.awk
BEGIN { RS="END:VCARD\\s*"; FS="\n"; OFS=":" }
{
    $0 = $0 gensub(/\s+$/,"",1,RT)

    for (i=1; i<=NF; i++) {
        name = gensub(/:.*/,"",1,$i)
        value = gensub(/.*:/,"",1,$i)
        n2v[name] = value
        names[i] = name
    }

    n2v["N"] = n2v["TEL;TYPE=CELLVOICE"] n2v["N"]

    for (i=1; i<=NF; i++) {
        name  = names[i]
        value = n2v[name]
        print name, value
    }
}

.

$ awk -f tst.awk file
BEGIN:VCARD
VERSION:2.1
N:07401000000;;
TEL;TYPE=CELLVOICE:07401000000
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:07401000001;;
TEL;TYPE=CELLVOICE:07401000001
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:07401000002;;
TEL;TYPE=CELLVOICE:07401000002
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:07401000003;;
TEL;TYPE=CELLVOICE:07401000003
END:VCARD

The above uses GNU awk for gensub(), multi-char RS, and RT and the basic (and idiomatic) idea is to split the input into records that end with END:VCARD and for each record first create an array (n2v[]) that maps field names (the part before the first : on each line) to their values (the part after the first :) and then you can just manipulate every field by it's name so you can trivially change values, rearrange the order, fill in defaults, etc. etc.

like image 79
Ed Morton Avatar answered Sep 06 '26 08:09

Ed Morton