如何避免使用 sed 的插入模式应用于字符串的中间?

如何避免使用 sed 的插入模式应用于字符串的中间?

目的

目的是转换以下字符串:

hello_hello,123-world567-helloworld123456,world1234-hello09876

使用 sed 转换为特定格式。

尝试

sed -e 's|^\(hello_[a-z0-9]\{3\}\)\(.*\)|\1,\1\2|g;s|..|&/|g' /tmp/file

预期结果

he/ll/o_/he/ll/o,123-world567-helloworld123456,/wo/rl/d1/23/4-/he/ll/o0/98/76/

目前的结果

问题是每/插入2个字符a。/应避免在两个逗号之间的部分插入 of 。

he/ll/o_/he/ll/o,/12/3-/wo/rl/d5/67/-h/el/lo/wo/rl/d1/23/45/6,/wo/rl/d1/23/4-/he/ll/o0/98/76/

答案1

我可以这样做:

sed 's|\(,[^,]*,\)\{0,1\}\([^,]\{1,2\}\)|\1/\2|g
' <<\IN                                     
hello_hello,123-world567-helloworld123456,world1234-hello09876
IN

...打印...

/he/ll/o_/he/ll/o,123-world567-helloworld123456,/wo/rl/d1/23/4-/he/ll/o0/98/76

所以最多所做的更改是对第二个s///替换进行的 - 但那是因为我删除了所有第一个替换。

所以你问题的最大部分是你只是告诉在每两个字符后面sed替换一个-/.-点意味着任何字符g全局的意思 - 或全部

第二重要的部分是第一次替换对你没有帮助 - 并且完全没有必要。

不仅如此,您还在第一个替换中插入了一个额外的逗号 - 所以在我弄清楚第一个位之后,我仍然遇到了额外的字段。看:

\(,[^,]*,\)\{0,1\}\([^,]\{1,2\}\)|\1/\2

这就是对我有用的替换语句,原因如下:

  • \(,[^,]*,\)\{0,1\}- 在全球范围内,您必须小心谨慎,只获得您需要的数量。你替换了每两个字符,所以这就是你得到的 -sed贪婪的。首先引用这一点 - 这很重要 - 因为sed从左到右读取时,通常只会在每两个连续的非逗号字符之间插入斜杠,但如果遇到逗号,它将读入到找到的下一个逗号并保存整个块根本\1不插入任何斜杠。

  • \([^,]\{1,2\}\)- 此处不能使用.点 - 它们将与逗号匹配,因此在跳过分隔符后,您只需在斜杠中写入即可。您需要明确排除逗号。这就是它的作用 - 其中 1 个或 2 个的每个序列 - 但sed总是会拉出它可能的数字中最大的一个。

我可以看到这个和你的例子之间的一个区别是这里的第一个斜杠位于字符串的头部,并且没有尾部斜杠,而你的则相反。为了解决这个问题,根据需要:

...;s|^/\(.*/.\)/*$|\1/|...

答案2

我确信有人会想出一种纯粹的sed方法,但我发现使用一个能够理解输入字段而不仅仅是行的程序对于此类事情来说要容易得多:

  1. 珀尔

    $ perl -F, -lane 'for($F[0],$F[2]){s|(..)|\1/|g;} print join ",",@F' /tmp/file 
    he/ll/o_/he/ll/o,123-world567-helloworld123456,wo/rl/d1/23/4-/he/ll/o0/98/76/
    

    解释

    • -a:将每个输入行拆分为字段并将它们保存在@F数组中。第 1dt 字段将为$F[0]、第二个字段,$F[1]依此类推。
    • -F:将字段分隔符设置为,
    • -n-e:读取每个输入行 ( -n) 并应用由 给出的脚本-e
    • -l:删除尾随换行符并\n在每个print调用中添加 a 。
    • for($F[0],$F[2]){}:将此应用于第一和第三字段。
    • s|(..)|\1/|g;:简单替换,它将/在每隔一个字符后添加一个。
    • print join ",",@F':用逗号连接字段列表并打印。由于字段已在上一步中更改,因此这将打印更改的字段。
  2. GNU awk

    $ awk -F, -v OFS="," '{$1=gensub(/(..)/,"\\1/","g",$1); $3=gensub(/(..)/,"\\1/","g",$3);}1;' /tmp/file 
    he/ll/o_/he/ll/o,123-world567-helloworld123456,wo/rl/d1/23/4-/he/ll/o0/98/76/
    

    解释

    如上所述,-F设置字段分隔符。-v OFS=","将输出分隔符设置为,。然后,gensub()函数(我相信仅限 GNU awk)运行替换。在这里,它在第一和第三字段上运行。

相关内容