Straight to the point, I\'m wondering how to use grep/find/sed/awk to match a certain string (that ends with a number) and increment that number by 1. The closest I\'ve come
This is ugly (I'm a little rusty), but here's a start using sed:
orig="something1" ;
text=`echo $orig | sed "s/\([^0-9]*\)\([0-9]*\)/\1/"` ;
num=`echo $orig | sed "s/\([^0-9]*\)\([0-9]*\)/\2/"` ;
echo $text$(($num + 1))
With an original filename ($orig
) of "something1", sed splits off the text and numeric portions into $text
and $num
, then these are combined in the final section with an incremented number, resulting in something2
.
Just a start since it doesn't consider cases with numbers within the file name or names with no number at the end, but hopefully helps with your original goal of using sed.
This can actually be simplified within sed by using buffers, I believe (sed can operate recursively), but I'm really rusty with that aspect of it.
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...
Pure sed version:
This version has no dependencies on other commands or environment variables.
It uses explicit carrying. For carry I use the @ symbol, but another name can be used if you like. Use something that is not present in your input file.
First it finds SEARCHSTRING<number>
and appends a @ to it.
It repeats incrementing digits that have a pending carry (that is, have a carry symbol after it: [0-9]@
)
If 9 was incremented, this increment yields a carry itself, and the process will repeat until there are no more pending carries.
Finally, carries that were yielded but not added to a digit yet are replaced by 1.
sed "s/SEARCHSTRING[0-9]*[0-9]/&@/g;:a {s/0@/1/g;s/1@/2/g;s/2@/3/g;s/3@/4/g;s/4@/5/g;s/5@/6/g;s/6@/7/g;s/7@/8/g;s/8@/9/g;s/9@/@0/g;t a};s/@/1/g" numbers.txt
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
' *