【问题标题】:Replace part of a line containing tabs替换包含制表符的行的一部分
【发布时间】:2023-03-21 11:08:01
【问题描述】:

如何将包含制表符 (\t) 的行的一部分替换为 sed、awk 或任何其他?

线

    <property name="systemVersionDsvTes"                value="xxx"/>

\t<property name=\"systemVersionDsvTes\"\t\t\t\tvalue=\"xxx\"/>

应该替换为:

    <property name="systemVersionDsvTes"                value="yyy"/>

\t<property name=\"systemVersionDsvTes\"\t\t\t\tvalue=\"yyy\"/>

xxx 可以变化,属性名称前有 一个标签,值前有 四个标签。此名称-值对是 xml 文件中唯一的一对。

我尝试了以下方法:

ACTUAL_VERSION="\t<property name=\"systemVersionDsvTes\"\t\t\t\tvalue=\"4.1.9\"/>"
NEW_VERSION="\t<property name=\"systemVersionDsvTes\"\t\t\t\tvalue=\"4.1.10\"/>"
sed -i -e "s/$ACTUAL_VERSION/$NEW_VERSION/g" buildSIM.xml

这导致了一个错误:sed: -e expression #1, character 65: Unknow option for command `s' (s///?)

表达有什么问题?使用 GNU sed。

【问题讨论】:

    标签: regex bash replace sed awk


    【解决方案1】:

    错误信息Unknow option for command 's' (s///?) 指的是可以在第三个斜杠之后添加到替换命令的“选项”。这些选项包括像g 这样的字母来替换所有出现的模式,而不仅仅是第一个,或者i 来忽略字母大小写。它通常暗示模式或替换包含额外的斜杠。为了处理模式中的斜线或替换,它们也必须被转义:

    ACTUAL_VERSION="\t<property name=\"systemVersionDsvTes\"\t\t\t\tvalue=\"4.1.9\"\/>"
    NEW_VERSION="\t<property name=\"systemVersionDsvTes\"\t\t\t\tvalue=\"4.1.10\"\/>"
    

    您也可以使用其他字符而不是斜杠作为分隔符,例如#:

    sed -i -e "s#$ACTUAL_VERSION#$NEW_VERSION#g" buildSIM.xml
    

    【讨论】:

    • 转义两个正斜杠可修复错误,然后 sed 按预期工作:sed -i -e "s/$ACTUAL_VERSION/$NEW_VERSION/g" file
    【解决方案2】:

    好心,用awk就行了,不用担心那些sed的废话:

    $ cat file
            <property name="systemVersionDsvTes"                            value="4.1.9"/>
    
    $ ACTUAL_VERSION='\t<property name="systemVersionDsvTes"\t\t\t\tvalue="4.1.9"/>'
    
    $ NEW_VERSION='\t<property name="systemVersionDsvTes"\t\t\t\tvalue="4.1.10"/>'  
    
    $ awk -v act="$ACTUAL_VERSION" -v new="$NEW_VERSION" '{gsub(act,new)}1' file   
            <property name="systemVersionDsvTes"                            value="4.1.10"/>
    

    实际上,您可能希望使用您采用的任何方法来转义 ACTUAL_VERSION 值中的“.”,因为它们匹配任何字符而不是文字“.”。或者,在 awk 中,您可以更改为使用字符串比较而不是 RE 比较:

    $ awk -v act="$ACTUAL_VERSION" -v new="$NEW_VERSION" 'start=index($0,act) { $0=substr($0,1,start-1) new substr($0,start+length(act)) }1' file   
            <property name="systemVersionDsvTes"                            value="4.1.10"/>
    

    在 sed 中没有等价物。

    【讨论】:

    • 感谢您提供如此详细的回答。我刚刚测试了这两种解决方案,它们运行良好。如果我转义“.”,awk 会向我发送警告,但仍然有效。
    • 您没有说您运行了什么代码或警告是什么,所以这是猜测,但您可能尝试了 gsub() 解决方案并且 awk 可能告诉您需要 2 个反斜杠来转义“。” (例如“4\\.1\\.9”)在指定为字符串的 RE 中,因为字符串被解析两次,读取时解析一次,执行时再次解析。您不需要,因此不应在 index() 解决方案中转义 '.",因为 index() 适用于字符串匹配,而不是 RE。
    • 只使用一个反斜杠 "4\.1\.9" gsub() 和 index() 返回相同的警告或警报,即(翻译自 pt-br):"awk: warning: escape sequence '\.' treated as normal '.'。使用 2 个反斜杠“4\\.1\\.9”,gsub() 变为“4\.1\.10”,index() 工作正常。
    • index() 与4\\.1\\.9 配合得很好。问题是 - 在给定的上下文中,4\\.1\\.9 是什么意思?当存储在稍后在 RE 上下文中使用的变量中时(例如使用 ~ variablematch(...,variable)),它表示 5 个字符 4 . 1 . 9。当存储在稍后在字符串上下文中使用的变量中时(例如使用index()),它表示9 个字符4 \ \ . 1 \ \ . 9 。当在常量 RE 上下文中使用时(例如 ~ /.../),它表示 7 个字符 4 \ . 1 \ . 9。所以这一切都“有效”,但你需要了解你告诉 awk 做什么才能得到你想要的。
    猜你喜欢
    • 1970-01-01
    • 2018-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    • 2010-11-07
    • 2022-07-18
    相关资源
    最近更新 更多