Here is an interesting problem: write a sed script to substitute every even number with the nearest odd number. I got this one from the list of the latest searches which bring the users to this site.
Let’s see how we can tackle this issue.
First of all, let me say that, for this type of tasks, I’d rather use awk because it has variables and strong and fast arithmetics capabilites. Here is how I’d do in awk:
$ echo "12 15 4 98 1123" | awk '{
for (i=1;i<=NF;i++)
if ($i ~ /^[0-9]*[02468]$/)
$i--
} 1'
The above command ignores numbers mixed with strings (i.e. foo12, 4bar2,etc.) and simply subtracts 1 from any even number (though you can easily add 1 by using $i++ in the place of $i–).
On the other hand, with sed we neither have variables nor any addition/subtraction operator, therefore we have to use a totally different algorithm:
$ echo "10 foo 16 bar 0 cat 98 day 1123" | sed 's/\<\([0-9]*\)0\>/\11/g;
s/\<\([0-9]*\)2\>/\13/g;
s/\<\([0-9]*\)4\>/\15/g;
s/\<\([0-9]*\)6\>/\17/g;
s/\<\([0-9]*\)8\>/\19/g;'
The above script1 considers five different cases: the number ends with a 0, with a 2, with a 4, with a 6 or with a 8. In every case, it substitutes the least significant digit with the odd number just above. For example,
s/\<\([0-9]*\)2\>/\13/g;
convert every number ending with 2 to the same number but with 3 as the last digit.
By the way, I craftily added one unit to all the even numbers because I didn’t want to solve the problem of decrementing a number ending with a 0: 10 -> 9, 100 -> 99, 1000 -> 999 etc.
It shouldn’t be too much harder but I bet it’d be a bit longer
()
Reference: