rturrado
rturrado

Reputation: 8074

Increment the numbers of all the terms in a string that follow a pattern

I have something like a printf string, e.g. The %1 fox jumped over the %2 bridge. And I want to transform it into The {0} fox jumped over the {1} bridge. That is: get string, search for %(\d+) pattern, turn it into {$1 - 1}, repeat until the end of the string.

Is there an easy way to do that in linux (an awk one-liner for example)? I have read that sed shouldn't be an option here.

IMPORTANT: ah, unfortunately, I can't use perl solutions!

NOTES: although I think I won't find tricky stuff in my strings like %% or %%1, I was trying to implement a solution dealing with that. A test string I was using is %1 aaa %%2 bbb%%% ccc%3.,45--- ddd%6%7. For this test string, I would expect the output {0} aaa %{1} bbb%%% ccc{2}.,45--- ddd{5}{6}

Upvotes: 0

Views: 143

Answers (3)

Jose Ricardo Bustos M.
Jose Ricardo Bustos M.

Reputation: 8174

Another solution using sed

sed -r '
    # replace all leading 0s by _
    :d; s/%([0-9]+)0(\b|_)/%\1_\2/g; td; 
    # decrement last digit only
    s/%([0-9]*)1(\b|_)/%\10\2/g; 
    s/%([0-9]*)2(\b|_)/%\11\2/g; 
    s/%([0-9]*)3(\b|_)/%\12\2/g; 
    s/%([0-9]*)4(\b|_)/%\13\2/g; 
    s/%([0-9]*)5(\b|_)/%\14\2/g; 
    s/%([0-9]*)6(\b|_)/%\15\2/g; 
    s/%([0-9]*)7(\b|_)/%\16\2/g; 
    s/%([0-9]*)8(\b|_)/%\17\2/g; 
    s/%([0-9]*)9(\b|_)/%\18\2/g; 
    #remove zero to left
    s/%0(_+)/%\1/g; 
    #replace _ by 9s
    :a; s/%([0-9]+)_(\b|_)/%\19\2/g; ta;
    s/%([0-9]+)/{\1}/g;'

you get

he {0} fox jumped over the {1} bridge.

and

{0} aaa %{1} bbb%%% ccc{2}.,45--- ddd{5}{6}

Upvotes: 2

123
123

Reputation: 11246

Another awk way

awk '{while(sub(/%[0-9]+/,"{"x++"}"));}!(x=0)' file

Just loops through the line incrementing x every match of %[number] and subbing. Sets x back to 0 at the end of line


For the new test string you can use

awk '{while(match($0,/%([0-9]+)/,a))sub(a[0],"{"a[1]-1"}")}1' file

Although this requires GNU awk for the third argument to match.

Example

%1 aaa %%2 bbb%%% ccc%3.,45--- ddd%6%7

becomes

{0} aaa %{1} bbb%%% ccc{2}.,45--- ddd{5}{6}

Upvotes: 4

fedorqui
fedorqui

Reputation: 290525

tAssuming no other % appear and they are sequential:

awk -F"%" -v OFS="{" '{for (i=1;i<=NF;i++) sub(i-1,i-2"}",$i)}1' file

This plays with the field separator setting it to %. This way, the field are split like this:

The %1 fox jumped over the %2 bridge
^^^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^
 $1             $2             $3

and then it is a matter of replacing the number i with i-1 whenever reading the field i+1. Finally, we glue it together by setting the output field separator to {.

Test

$ cat a
The %1 fox jumped over the %2 bridge
The %1 fox jumped over the %2 bridge and %3 other %4 things
$ awk -F"%" -v OFS="{" '{for (i=1;i<=NF;i++) sub(i-1,i-2"}",$i)}1' a
The {0} fox jumped over the {1} bridge
The {0} fox jumped over the {1} bridge and {2} other {3} things

Note I am using sub() because it replaces just once. This way, we make sure other occurrences of 1 won't be changed when modifying the field $1, etc.

From the link above:

sub(regexp, replacement, target)

The sub function alters the value of target. It searches this value, which should be a string, for the leftmost substring matched by the regular expression, regexp, extending this match as far as possible. Then the entire string is changed by replacing the matched text with replacement. The modified string becomes the new value of target.

Upvotes: 2

Related Questions