Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
612 views
in Technique[技术] by (71.8m points)

awk - Only replace patterns if they are found inside the function definitions of functions specified in a variable

Hey I have this script that replaces occurances of a pattern inside files.

gawk -v RS='<[^>. ]+>' '{ ORS="" }  
    RT {                 
       switch (RT)
       {  
            case /E:/:
                if (!(RT in events))                    
                    events[RT] = eventCount++   
                name=RT    
                sub(/<E:/, "", name)
                sub(/>/, "", name)
                ORS=name ", " events[RT]
                break
           ...
       }                
    }
    {
       print $0 > (FILENAME ".c")
    }' $files

I need to modify the script in a way so that it only replaces the patterns if they are found inside the function definitions of the functions that I specify in a variable. For example:

gawk -v RS='<[^>. ]+>' -v FUNCS='a(void) b(void) foobar(void)''{ ORS="" }  
    RT {    
       #if inside of one of FUNCS                                         
       switch (RT)
       {
            case /E:/:
                if (!(RT in events))                    
                    events[RT] = eventCount++   
                name=RT    
                sub(/<E:/, "", name)
                sub(/>/, "", name)
                ORS=name ", " events[RT]
                break
           ...
       }                
    }
    {
       print $0 > (FILENAME ".c")
    }' foo.c bar.c

foo.c:

void a(void)
{
    <E:X>
}

void b(void)
{
    <E:Y>
}

void barfoo(void)
{
    <E:Z>
}

bar.c:

void c(void)
{
    <E:A>
}

void foo_bar(void)
{
    <A:B>
}

after running the script the files should look like this:

foo.c:

void a(void)
{
    0
}

void b(void)
{
    1
}

void barfoo(void)
{
    <E:Z>
}

bar.c:

void c(void)
{
    <E:A>
}

void foo_bar(void)
{
    2
}

Edit: I have a problem with the current solution because it doesnt work in some functions. The example code I am testing against:

test.c

void foobar_(void)
{
    <E:X>
}


void foobar(void)
{
    <E:X>
}

test.c.tmp

void foobar_(void)
{
    <E:X>
}


void foobar(void)
{
    <0>
}

and the code that I run:

awk -v funcs='foobar(void) foobar_(void)' '
BEGIN {
    split(funcs,tmp)
    for (i in tmp) {
        fnames[tmp[i]]
    }
}
/^[[:space:]]*[[:alnum:]_]+[[:space:]]*[[:alnum:]]+([^)]*)/ {
    inFunc = ($NF in fnames ? 1 : 0)
}
{
    head = ""
    tail = $0
    while ( inFunc && match(tail,/<E:[^>]+>/) ) {
        tgt = substr(tail,RSTART+1,RLENGTH-2)
        if ( !(tgt in map) ) {
            map[tgt] = cnt++
        }
        head = head substr(tail,1,RSTART) map[tgt]
        tail = substr(tail,RSTART+RLENGTH-1)
    }
    $0 = head tail
}
{
    print $0 > (FILENAME ".tmp")
}' $module_files
question from:https://stackoverflow.com/questions/65935347/only-replace-patterns-if-they-are-found-inside-the-function-definitions-of-funct

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

With GNU awk (which you're already using) for the 3rd arg to match() and s/w shorthand:

$ cat tst.awk
BEGIN {
    split(funcs,tmp)
    for (i in tmp) {
        fnames[tmp[i]]
    }
}
/^s*w+s*w+([^)]*)/ {
    inFunc = ($NF in fnames ? 1 : 0)
}
inFunc && match($0,/(.*)(<[^>]+>)(.*)/,a) {
    $0 = a[1] (cnt++) a[3]
}
{ print }

or with any awk:

$ cat tst.awk
BEGIN {
    split(funcs,tmp)
    for (i in tmp) {
        fnames[tmp[i]]
    }
}
/^[[:space:]]*[[:alnum:]_]+[[:space:]]*[[:alnum:]_]+([^)]*)/ {
    inFunc = ($NF in fnames ? 1 : 0)
}
inFunc && match($0,/<[^>]+>/) {
    $0 = substr($0,1,RSTART-1) (cnt++) substr($0,RSTART+RLENGTH+1)
}
{ print }

$ awk -v funcs='a(void) b(void) foobar(void)' -f tst.awk foo.c
void a(void)
{
    0
}

void b(void)
{
    1
}

void barfoo(void)
{
    <E:Z>
}

To have the count keep incrementing across input files use gawk -i inplace to run on multiple files at once updating them as you go or print to temp files as you go and then move them when the script is done to replace the originals or similar.


EDIT: showing the above work for a function name that includes an underscore:

$ cat bar.c
void c(void)
{
    <E:A>
}

void foo_bar(void)
{
    <A:B>
}

$ awk -f tst.awk -v funcs='foo_bar(void)' bar.c
void c(void)
{
    <E:A>
}

void foo_bar(void)
{
    0
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...