Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using sed to replace numbers

Tags:

regex

macos

sed

I'd like to replace some numbers in a file with the result of a calculation using the found number, and like to use sed on MacOSX. I've tried a lot of variants and now know I have to use -E to use modern instead of basic regular expression.

Some examples:

echo "bla 18934750 + wwv_flow_id.offset bla" | sed s/\ +\ wwv_flow_id.offset/blabla/

gives

bla 18934750blabla bla

So without the -E, it finds and replaces fixed text. But with the -E, it doesn't:

echo "bla 18934750 + wwv_flow_id.offset bla" | sed -E s/\ +\ wwv_flow_id.offset/blabla/

gives

bla 18934750 + wwv_flow_id.offset bla

In other words: no match and no change in the text. The ultimate goal is to find the number that precedes the fixed text " + wwv_flow_id.offset" and use that number and subtract a fixed number (say 750) from it, so the end result becomes:

bla 18934000 + wwv_flow_id.offset bla

And for that I need at least back references, which also don't work like I expected, because

echo "bla 18934750 + wwv_flow_id.offset bla" | sed -E s/\([0-9]+\)\ /\1/

gives

bla 1+ wwv_flow_id.offset bla

I hope some regex guru can help me out here.


UPDATE

With the help of ruakh, this is what I've got now:

echo "bla 18934750 + wwv_flow_id.offset bla" | sed -E 's/([0-9]+) \+ wwv_flow_id.offset/(\1-750) \+ wwv_flow_id.offset/'

which returns:

bla (18934750-750) + wwv_flow_id.offset bla

The bonus question now is, how to turn this into

bla 18934000 + wwv_flow_id.offset bla




UPDATE 2

I managed to achieve my desired result by combining sed with awk, like this:

echo "bla 18934750 + wwv_flow_id.offset bla" | sed -E 's/([0-9]+)([ ]*)\+([ ]*)wwv_flow_id.offset/~\1~\2\+\3wwv_flow_id.offset/' | awk -F~ '{print $1 $2-750 $3}'

(I know for sure there are no ~ tokens on the original line)

like image 789
Rob van Wijk Avatar asked Sep 30 '12 14:09

Rob van Wijk


1 Answers

In "modern" regexes, + has a special meaning — it means "one or more" (just like how * means "zero or more") — so to match an actual plus sign, you need to use \+. Since you apparently prefer not to wrap your sed-script in quotes, you would write it as \\+:

echo "bla 18934750 + wwv_flow_id.offset bla" | sed -E s/\ \\+\ wwv_flow_id.offset/blabla/

though I think it will make your life easier if you abandon that preference, and write:

echo "bla 18934750 + wwv_flow_id.offset bla" | sed -E 's/ \+ wwv_flow_id.offset/blabla/'

Quoting your argument will also address your back-reference issue, whereby Bash is translating \1 to 1:

echo "bla 18934750 + wwv_flow_id.offset bla" | sed -E 's/([0-9]+) /\1/'

though if you still prefer to stick with your non-quoted-sed-script style, you could write \\1:

echo "bla 18934750 + wwv_flow_id.offset bla" | sed -E s/\([0-9]+\)\ /\\1/
like image 146
ruakh Avatar answered Sep 23 '22 18:09

ruakh