使用sed,如何进一步改造group back references?

Using sed, how can group back references be further transformed?

我可以使用 sed 将正则表达式匹配替换为正则表达式中组反向引用的转换吗?

问题

假设我要替换以下形式的字符串:

(@ -p <fqdn>)

每行可能有多个这样的匹配项。

与:

<fqdn with dots replaced by underscores>

例子

com.xyz (@ -p com.abc.def) com.pqr.stu (@ -p com.ghi)

会变成:

com.xyz com_abc_def com.pqr.stu com_ghi

想法

要开始寻求解决方案,请考虑:

$ sed 's|(@ -p \([^)]*\))||' <<<"com.xyz (@ -p com.abc) com.pqr (@ -p com.ghi)"
com.xyz com.abc com.pqr com.ghi

这会进行适当的选择;但是,现在我仍然需要用 s|\.|_|g.

转换 部分

任何人都可以展示如何使用 sed 完成此操作吗?

我的环境是 bash 4.2.46(1)-release, CentOS 7.3.1611.

备注:

您可以使用 gnu awk:

s='com.xyz (@ -p com.abc.def) com.pqr.stu'
awk -v RS='\(@ -p [^)]+\)' '{
       ORS=gensub(/.* |\)/,"","g",gensub(/\./,"_","g",RT))} 1' <<< "$s"

com.xyz com_abc_def com.pqr.stu

gawk 解决方案:

str="com.xyz (@ -p com.abc.def) com.pqr.stu"
awk 'match([=10=], /\(@ -p ([^)]+)\)/, a){ "echo "a[1]" | tr \".\" \"_\"" | getline v; 
     sub(/\(@ -p ([^)]+)\)/,v, [=10=]); print }' <<< $str

输出:

com.xyz com_abc_def com.pqr.stu

如果目标字符串只出现一次(每行输入), 你可以使用 hold space 来做双重替换,像这样:

单个替换

#Copy input line to the hold space: A(@B)C -- A(@B)C
h

#Replace the target substring with (@) (a "marker" string): A(@)C -- A(@B)C 
s/(@ -p [^)]*)/(@)/

#Exchange the content of the pattern space and hold space: A(@B) -- A(@)C 
x

#Strip off anything except the target substring value: B -- A(@)C
s/.*(@ -p \([^)]*\)).*//

#Modify the target substring as appropriate: B' -- A(@)C
y/./_/

#Append the content of the hold space back to the pattern space: B'\nA(@)C -- 
G

#Merge the lines, replacing the "marker" string with the processed value: AB'C
s/\(.*\)\n\(.*\)(@)//

示例输出:

%echo "com.xyz (@ -p com.abc) com.pqr" | sed -f doublereplace.sed 
com.xyz com_abc com.pqr

多次替换

循环版本如下所示:

#Loop label 
:start /(@/ { 
    #Copy input line to the hold space: A(@B)C -- A(@B)C
    h

    #Replace the target substring with (@) (a "marker" string): A(@)C -- A(@B)C 
    s/(@ -p [^)]*)/(@)/

    #Exchange the content of the pattern space and hold space: A(@B) -- A(@)C 
    x

    #Strip off anything except the target substring value: B -- A(@)C
    s/[^(]*(@ -p \([^)]*\)).*//

    #Modify the target substring as appropriate: B' -- A(@)C
    y/./_/

    #Append the content of the hold space back to the pattern space: B'\nA(@)C -- 
    G

    #Merge the lines, replacing marker string with the processed value: AB'C
    s/\(.*\)\n\(.*\)(@)//

    #Loop
    b start
}

示例输出:

%echo "com.xyz (@ -p com.abc.def) com.pqr.stu (@ -p com.ghi)" |
sed -f doublereplace.sed

com.xyz com_abc_def com.pqr.stu com_ghi

硬化

更可靠的版本可能使用换行符作为 separators/marker string:

#Loop label 
:start /(@ -p [^)]*)/ { 
    #Copy input line to the hold space: A(@B)C -- A(@B)C
    h

    #Replace the target substring with (@) (a "marker" string): A\nC -- A(@B)C 
    s/(@ -p [^)]*)/\n/

    #Exchange the content of the pattern space and hold space: A(@B)C -- A\nC 
    x

    #Isolate the first instance of a target substring to a separate line A\n(@B)\nC -- A\n\C 
    s/\((@ -p [^)]*)\)/\n\n/1

    #Strip off anything except the target substring value: B -- A\nC
    s/.*\n(@ -p \([^)]*\))\n.*//

    #Modify the target substring as appropriate: B' -- A\nC
    y/./_/

    #Append the content of the hold space back to the pattern space: B'\nA\nC -- 
    G

    #Merge the lines, replacing marker string with the processed value: AB'C
    s/\(.*\)\n\(.*\)\n//

    #Loop
    b start
}

这将允许输入数据中任何不完整的 @() 构造, 喜欢 (@ t.i.m.e.s):

%echo "com.xyz (@ -p com.abc.def) fails (@ t.i.m.e.s) com.pqr.stu (@ -p com.ghi)" |
sed -f doublereplace.sed

com.xyz com_abc_def fails (@ t.i.m.e.s) com.pqr.stu com_ghi