How to find/replace and increment a matched number with sed/awk?

Solution 1:

I think finding file isn't the difficult part for you. I therefore just go to the point, to do the +1 calculation. If you have gnu sed, it could be done in this way:

sed -r 's/(.*)(\?cache_version=)([0-9]+)(.*)/echo "\1\2$((\3+1))\4"/ge' file

let's take an example:

kent$  cat test 
ello
barbaz?cache_version=3fooooo
bye

kent$  sed -r 's/(.*)(\?cache_version=)([0-9]+)(.*)/echo "\1\2$((\3+1))\4"/ge' test     
ello                                                                             
barbaz?cache_version=4fooooo
bye

you could add -i option if you like.

edit

/e allows you to pass matched part to external command, and do substitution with the execution result. Gnu sed only.

see this example: external command/tool echo, bc are used

kent$  echo "result:3*3"|sed -r 's/(result:)(.*)/echo \1$(echo "\2"\|bc)/ge'       

gives output:

result:9

you could use other powerful external command, like cut, sed (again), awk...

Solution 2:

This perl command will search all files in current directory (without traverse it, you will need File::Find module or similar for that more complex task) and will increment the number of a line that matches cache_version=. It uses the /e flag of the regular expression that evaluates the replacement part.

perl -i.bak -lpe 'BEGIN { sub inc { my ($num) = @_; ++$num } } s/(cache_version=)(\d+)/$1 . (inc($2))/eg' *

I tested it with file in current directory with following data:

hello
cache_version=3
bye

It backups original file (ls -1):

file
file.bak

And file now with:

hello
cache_version=4
bye

I hope it can be useful for what you are looking for.


UPDATE to use File::Find for traversing directories. It accepts * as argument but will discard them with those found with File::Find. The directory to begin the search is the current of execution of the script. It is hardcoded in the line find( \&wanted, "." ).

perl -MFile::Find -i.bak -lpe '

    BEGIN { 
        sub inc { 
            my ($num) = @_; 
            ++$num 
        }

        sub wanted {
            if ( -f && ! -l ) {  
                push @ARGV, $File::Find::name;
            }
        }

        @ARGV = ();
        find( \&wanted, "." );
    }

    s/(cache_version=)(\d+)/$1 . (inc($2))/eg

' *