Double a number with sed
by shruggy from LinuxQuestions.org on (#5Q21Y)
First, some context.
There is an old, but very good vi/ex tutorial series by Walter Alan Zintz. Recently, I stumbled upon a modernized version of it, adapted for Vim usage by Barry Arthur and Israel Chauca: https://dahu.github.io/vim_waz_ere
It's still incomplete, but already useful. Among other topics, they expanded the chapter on the substitute command and even provided some Vim-specific exercises not found in the original series.
One of those exercises deals with the following dataset:
Code:@items = [
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15]
]There are two questions:
1. What command would remove the 3rd column of numbers?
2. What command would double the numbers in the third column instead of removing them?
My first idea was to use substitute with the e flag, like this:
Code:sed -E '
/^\s*\[/{
h
s/^([^,]+,){2}([^,]+).*/expr \2 + \2/e
G
s/^(.+)\n(([^,]+,){2}\s*)[^,]+/\2\1/
}'This is probably as practical as it goes with sed.
Of course, there is plenty of information at http://sed.sf.net on the subject of doing arithmetic in sed proper, but it's anything other than practical. Nevertheless, after some thought (and using that site as reference) I came up with this:
Code:#!/bin/sed -Ef
# Double a natural number
# Carry is indicated by upper case
y/0123456789/abcdeFGHIJ/
s/^[F-J]/A&/
s/[a-e][F-J]/\u&/g
s/[F-J]([a-eA-E]|$)/\l&/g
y/abcdefghijABCDEFGHIJ/02468024681357913579/For an example like above, where all numbers are less than 50, this will do as well:
Code:#!/bin/sed -Ef
# Quick'n'dirty doubling of a number
# Only numbers up to 49 are allowed
/^[0-4]?[0-9]$/!{
i\
Number out of range. Acceptable are numbers 0 - 49.\
Invalid input occured at this line:
=
q
}
s/[5-9]/0&/
y/123456789/246802468/
s/.?0.$/&0123456789/
s/(.).?(.).*\1(.).*/\3\2/What do you think?
There is an old, but very good vi/ex tutorial series by Walter Alan Zintz. Recently, I stumbled upon a modernized version of it, adapted for Vim usage by Barry Arthur and Israel Chauca: https://dahu.github.io/vim_waz_ere
It's still incomplete, but already useful. Among other topics, they expanded the chapter on the substitute command and even provided some Vim-specific exercises not found in the original series.
One of those exercises deals with the following dataset:
Code:@items = [
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15]
]There are two questions:
1. What command would remove the 3rd column of numbers?
Unlike sed, ed, edbrowse, vile and elvis, the substitute command in Vim doesn't have the number flag that allows you to specify the Nth match.There's a bonus question, too:
So an obvious sed/ed solution s/\s*\w*,//3 won't work in Vim. What they suggest instead is setting the start of the match, an equivalent to lookbehind:
Code:s/\v/(.{-},){2}\zs.{-},//A rather inelegant solution compared to sed/ed.
2. What command would double the numbers in the third column instead of removing them?
As a solution they suggest using substitute with an expression, a powerful, but somewhat esoteric Vim feature:That got me thinking whether a transformation like this was possible with sed, and what it would look like. Awk is certainly better suited for this kind of task, but that's beyond the point.
Code:s/\v%(\d+,\s*){2}\zs\d+/\=submatch(0)*2
My first idea was to use substitute with the e flag, like this:
Code:sed -E '
/^\s*\[/{
h
s/^([^,]+,){2}([^,]+).*/expr \2 + \2/e
G
s/^(.+)\n(([^,]+,){2}\s*)[^,]+/\2\1/
}'This is probably as practical as it goes with sed.
Of course, there is plenty of information at http://sed.sf.net on the subject of doing arithmetic in sed proper, but it's anything other than practical. Nevertheless, after some thought (and using that site as reference) I came up with this:
Code:#!/bin/sed -Ef
# Double a natural number
# Carry is indicated by upper case
y/0123456789/abcdeFGHIJ/
s/^[F-J]/A&/
s/[a-e][F-J]/\u&/g
s/[F-J]([a-eA-E]|$)/\l&/g
y/abcdefghijABCDEFGHIJ/02468024681357913579/For an example like above, where all numbers are less than 50, this will do as well:
Code:#!/bin/sed -Ef
# Quick'n'dirty doubling of a number
# Only numbers up to 49 are allowed
/^[0-4]?[0-9]$/!{
i\
Number out of range. Acceptable are numbers 0 - 49.\
Invalid input occured at this line:
=
q
}
s/[5-9]/0&/
y/123456789/246802468/
s/.?0.$/&0123456789/
s/(.).?(.).*\1(.).*/\3\2/What do you think?