Shell命令为属性键中的点包围的数字添加双引号

xghobddn  于 2023-05-29  发布在  Shell
关注(0)|答案(8)|浏览(183)

我有以下属性键

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

我需要将此转换为以下内容

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

嵌套的属性键中的所有数字都需要有双引号,这些数字具有pre或post或两者都具有点符号。不应更新键中与文本连接的数字。(level1.34access23=“true”=> no change)无法更新值。
我试过使用following,但它会给所有内容添加双引号。

sed -E 's/^([^=]+)\.([0-9]+)/\1."\2"/' input.properties

有人能帮帮我吗?

ep6jt1vc

ep6jt1vc1#

使用您显示的示例,请尝试以下在GNU awk中编写和测试的awk代码。

awk '
BEGIN{
  s1="\""
  FS=OFS="="
}
{
  first=$1
  while(match(first,/(\.|^)([0-9]+)(\.|$)/,arr)){
    sub(arr[2],s1 arr[2] s1,$1)
    first=substr($1,RSTART+RLENGTH)
  }
}
1
'  Input_file
jrcvhitl

jrcvhitl2#

请尝试以下操作:

sed -E 's/^([^=]+\.)?([0-9]+)(\.|=)/\1"\2"\3/' input.properties
  • ^([^=]+\.)?匹配零个或一个除等号后跟点以外的任何字符序列。zero的情况将匹配前导数字,例如88
  • (\.|=)匹配后面的点或等号。
    [编辑]

如果perl恰好是您的选择,那么:

perl -pe 's/(^|\.)(\d+)(?=\.|=)(?=.*=)/$1"$2"/g' input.properties

这在密钥中包含多个数字的情况下将起作用。

  • (^|\.)匹配字符串的开头或一个点,匹配的子字符串被$1引用为反向引用(就像\1在sed中所做的那样)。
  • (\d+)匹配一个数字。
  • (?=\.|=)是一个正的前瞻,它匹配一个点或等号,而不增加正则表达式的搜索位置。
  • (?=.*=)是一个正的前瞻,它匹配后跟等号的任何字符序列。它确保替换仅发生在密钥内。
  • g选项支持多个替换。
nzkunb0c

nzkunb0c3#

您的正则表达式显然也不需要在数字后面加一个点,并且与表达式开头的数字不匹配。
这里有一个简单的脚本,它用两边的点锚定数字,只要等号前还有未加引号的数字,就继续替换。

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

更详细地说

  • 匹配并替换第一个点前的一个数字。
s/^([0-9]+)\./"\1"./
  • 声明循环
:b

的标签

  • 匹配和替换点之间的数字
s/^([^=]+)\.([0-9]+)\./\1."\2"./
  • 如果我们执行了一个替换,循环回到标签
tb

,这确保了只要还有需要引用的数字,我们就可以继续下去。

  • 最后,处理等号前的一个数字。
s/^([^=]+)\.([0-9]+)=/\1."\2"=/

如果您在运行此脚本时遇到问题,请尝试将整个脚本放在单引号中:

sed -E -e 's/^([0-9]+)\./"\1"./
:b 
  s/^([^=]+)\.([0-9]+)\./\1."\2"./
  tb
  s/^([^=]+)\.([0-9]+)=/\1."\2"=/' input.properties

正如您所看到的,sed几乎是一种只写语言。下面是一个简单的Awk脚本,它实现了相同的逻辑。

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

这也有些浓缩,但并不难理解(如果您认为它太晦涩,Awk可以更容易地用手写写出内容)。

  • 在开始处理任何输入之前,将输入字段分隔符FS和输出字段分隔符OFS设置为等号。
BEGIN { FS=OFS="=" }

这会导致$1包含第一个等号之前的所有内容,而$2$3等包含后续的等号分隔字段。
然后,对于每个输入行

  • 将第一个字段$1(即第一个等号之前的所有内容)上的点到数组a中。数组的长度为n
{ n = split($1, a, ".");
  • 创建一个空字符串变量f,我们将在其中累积$1的替换值。
f=""
  • 循环遍历a中的字段,即从1到n
for(i=1; i<=n; ++i)
  • 在循环中,使用其他文本更新f
  • 如果f不为空,则在f的值后面追加一个点;否则,只保留一个空字符串,连接...
f = (f ? f "." : "")

这使用了三级运算符(expression ? value : othervalue),如果expression为真,则产生value,否则产生othervalue。因此,换句话说,如果f非空,则产生f的当前值和文字点"."的串联;否则,生成空字符串。

  • ...与另一个三级表达式的结果连接,它会产生a[i],带或不带双引号,这取决于它是否完全是数值。
(a[i] ~ /^[0-9]+$/ ? "\"" a[i] "\"" : a[i])
  • 在循环这些值之后,f将包含$1的前一个值,但a中的所有全数字字段都用双引号括起来。将其分配回$1
$1 = f
  • 最后,打印修改后的行。
}1

右大括号终止了我们对每一行输入所执行的操作,而‘1’只是一个简单的习惯用法,它会导致Awk打印每一行。
(In更详细地说,值1被应用为一个条件,该条件始终为真,没有动作,这意味着默认动作,即打印当前输入记录。

5q4ezhmt

5q4ezhmt4#

如果你对python没问题

"""
https://stackoverflow.com/questions/76320083/shell-command-to-add-double-quotes-for-number-surrounded-by-dots-in-property-key

$ cat /tmp/a
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 
"""

import re

with open("/tmp/a", "r") as reader:
    for line in reader.readlines():
        sline = line.split("=")
        m = re.sub(r"(^|\.)(\d+)(\.|$)", r"\1" + '"' + r"\2" + '"' + r"\3", sline[0])
        print("=".join([m, sline[1].strip()]))
bxpogfeg

bxpogfeg5#

使用GNU awk for gensub()

$ cat tst.awk
BEGIN { FS=OFS="=" }
{
    for (i=1; i<=2; i++) {
        $1 = gensub(/(^|\.)([0-9]+)(\.|$)/,"\\1\"\\2\"\\3","g",$1)
    }
    print
}
$ awk -f tst.awk file
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
foo."17"."18".bar=whatever

或者使用任何awk:

$ cat tst.awk
BEGIN { FS=OFS="=" }
{
    while ( match($1,/(^|\.)[0-9]+(\.|$)/) ) {
        tgt = substr($1,RSTART,RLENGTH)
        sub(/^\.?/,"&\"",tgt)
        sub(/\.?$/,"\"&",tgt)
        $1 = substr($1,1,RSTART-1) tgt substr($1,RSTART+RLENGTH)
    }
    print
}
$ awk -f tst.awk file
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
foo."17"."18".bar=whatever
ippsafx7

ippsafx76#

我的两分钱:

sed 's/=/\n/' | \
  sed '/./ {s/\([0-9][0-9]*\)/"\1"/g;N;s/\n/=/;}' input.properties

第一个sed将一个属性及其值拆分为两行。
第二个字符串在第一行(属性名)中用"字符包围数字,并连接值(来自第二行)。
如果多行上有值,则行为将不是预期的行为:命令行必须在同一唯一行中找到属性及其值。

ogsagwnx

ogsagwnx7#

无需捕获组

mawk 'gsub("(^|[.])[0-9]+([.]|$)", ".\"\3&\5\".",
             $!_) gsub("[.]*($|[\3\5][.]*)|^[.]",_, $!_)' FS== OFS==
cnwbcb6i

cnwbcb6i8#

awk 'BEGIN{FS=OFS="."}
    { 
      for(i=1;i<=NF;i++) {
        if(match($i, /[^0-9]=/)) break
        if(match($i, /^[0-9]+$|^[0-9]*=/)) sub(/^[0-9]+/,"\"&\"", $i)
      }
    }1
' file

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

相关问题