Issue
I have following property keys
connection.party= 0.0.0.0
remote.app.328.port= 5432
remote.server.url="https://someurl.com?q=78.78"
remote.conncetion.7="basic"
remote.52.base= "local"
level1.34access23="true"
88.location.code=24
location.234.code.52=24
I need this converted into following
connection.party= 0.0.0.0
remote.app."328".port= 5432
remote.server.url="https://someurl.com?q=78.78"
remote.conncetion."7"="basic"
remote."52".base= "local"
level1.34access23="true"
"88".location.code=24
location."234".code."52"=24
all digits in nested property keys which have pre or post or both dot signs need to have double quotes. Numbers joined with texts in keys should not be updated. (level1.34access23="true" => no change ) Values cannot be updated.
I tried with following , but it add double quotes to all.
sed -E 's/^([^=]+)\.([0-9]+)/\1."\2"/' input.properties
Can someone please help ?
Solution
Your regular expression obviously fails to require a dot after the number, too, and does not match numbers at the beginning of the expression.
Here is a simple script which anchors numbers with dots on both sides, and keeps on substituting as long as there are still unquoted numbers before the equals sign.
sed -E -e 's/^([0-9]+)\./"\1"./' \
-e :b -e 's/^([^=]+)\.([0-9]+)\./\1."\2"./' -e tb \
-e 's/^([^=]+)\.([0-9]+)=/\1."\2"=/' input.properties
In some more detail,
- Match and substitute a single number before the first dot.
s/^([0-9]+)\./"\1"./
- Declare a label for looping
:b
- Match and substitute numbers between dots
s/^([^=]+)\.([0-9]+)\./\1."\2"./
- If we performed a substitution, loop back to the label
This ensures that we keep going as long as there are still numbers which require quoting.tb
- Finally, handle a number immediately before the equals sign.
s/^([^=]+)\.([0-9]+)=/\1."\2"=/
If you are having trouble getting this to run, try putting the entire script in single quotes:
sed -E -e 's/^([0-9]+)\./"\1"./
:b
s/^([^=]+)\.([0-9]+)\./\1."\2"./
tb
s/^([^=]+)\.([0-9]+)=/\1."\2"=/' input.properties
As you can see, sed
is pretty much a write-only language. Here is a simple Awk script which implements the same logic.
awk 'BEGIN { FS=OFS="=" }
{ n = split($1, a, "."); f=""
for(i=1; i<=n; ++i)
f = (f ? f "." : "") (a[i] ~ /^[0-9]+$/ ? "\"" a[i] "\"" : a[i])
$1 = f}1' input.properties
This is somewhat condensed, too, but it's not too hard to wrap your head around (and if you think it's too obscure, Awk makes it easier to write things out in longhand).
Before we start processing any input, set the input field separator
FS
and the output field separatorOFS
to an equals sign.BEGIN { FS=OFS="=" }
This causes
$1
to contain everything before the first equals sign, and$2
,$3
, etc to contain subsequent equals-delimited fields.Then, for each input line
Split the first field
$1
(i.e. everything before the first equals sign) on dots into the arraya
. The length of the array is inn
.{ n = split($1, a, ".");
Create an empty string variable
f
where we will be accumulating the replacement value for$1
.f=""
Loop over the fields in
a
, i.e. from 1 throughn
:for(i=1; i<=n; ++i)
In the loop, update
f
with additional text.- If
f
is non-empty, append a dot after the value off
; otherwise, just keep an empty string, concatenated with ...
This uses the tertiary operatorf = (f ? f "." : "")
(expression ? value : othervalue)
which producesvalue
ifexpression
is true, otherwiseothervalue
. So, in other words, iff
is non-empty, yield the concatenation of the current value off
and a literal dot"."
; otherwise, yield an empty string. - ... concatenated with the result of another tertiary expression, which yields
a[i]
with or without double quotes, depending on whether it is entirely numerical.(a[i] ~ /^[0-9]+$/ ? "\"" a[i] "\"" : a[i])
- If
After looping over the values,
f
will contain the previous value of$1
but with any all-numeric fields froma
with double quotes around them. Assign this back to$1
.$1 = f
Finally, print the modified line.
}1
The closing brace terminates the action we have been performing for every input line, and the ´1` is just a simple idiom which causes Awk to print every line.
(In some more detail, the value
1
is applied as a condition, which is always true, with no action, which implies the default action, which is to print the current input record.)
Answered By - tripleee Answer Checked By - Mary Flores (WPSolving Volunteer)