查找并替换文件中的多行文件内容

时间:2012-09-15 14:02:53

标签: file replace sed newline multiline

我想做的是:

find some_files -name '*.html' -exec sed -i "s/`cat old`/`cat new`/g" {} \;

new 包含换行符和斜杠以及其他特殊字符,这会阻止sed正确解析。

我已经阅读了如何使用sed和命令 tr ,命令 printf'%q'来转义换行符,但是我无法使这些工作这可能是因为我不完全了解他们的功能。另外,我不知道哪些特殊字符我仍然需要逃脱才能上班。

1 个答案:

答案 0 :(得分:1)

我不确定你想要做什么,但如果旧文件包含换行符,你可能会遇到麻烦。这是因为sed通过在每一行上应用命令来工作,因此除非您明确加载更多行,否则尝试将行与表示多行的模式匹配将不起作用。

我的建议是在应用substitute命令之前将整个文件加载到sed的“buffer”中。然后,您必须确保旧的和新的正确转义。此外,更令人困惑的是,旧文件(模式)的转义必须与新文件(替换)的转换不同。

让我们首先将新文件转义为“new.tmp”文件。为清楚起见,我们将创建一个名为“escape_new.sed”的sed脚本:

#!/bin/sed -f

# Commas used as separators
s,\\,\\\\,g
s,$,\\,g
s,[/&],\\&,g
$ a/

然后运行它:sed -f escape_new.sed new > new.tmp

我们使用三个命令来逃避:

  1. 反斜杠应该以另一个反斜杠
  2. 开头
  3. 换行符之前应加一个反斜杠(我们通过在行尾之前添加一个反斜杠来实现)。
  4. &符号和斜杠前面应加一个反斜杠(请注意,替换文本中的&实际上是包含匹配的运算符,因此如果它与包含斜杠的斜杠相匹配,如果它与符号匹配,则为包含&符号。
  5. 在最后一行(引用“$”符号),我们追加(通过“a”命令)斜杠。这是我们稍后将使用的替换命令的结束斜杠。我们必须把它放在这里,因为反引号将在输入结束时删除任何额外的换行符,这可能会导致问题(例如,用于引用实际引用终止斜杠的换行符的反斜杠)。
  6. 现在让我们逃避旧文件。如上所述,我们将创建一个“escape_old.sed”脚本。在我们这样做之前,我们需要将整个文件加载到模式空间(sed的内部缓冲区)中,以便我们可以替换换行符。我们可以使用以下命令执行此操作:

    : a
    $! {
        N
        b a
    }
    

    第一个命令创建一个名为“a”的标签。第二个命令(“{”)实际上启动了一组命令。这里的魔力是“$!”地址前缀。该前缀告诉它只有在读取的最后一行输入行不是输入的最后一行时才运行命令(“$”表示输入的最后一行,“!”表示不是)。组中的第一个命令将输入​​中的下一行追加到模式空间中。如果在最后一行执行此“N”命令,它将终止脚本,因此我们必须小心不要在最后一行执行它。组中的第二个命令是分支命令“b”,它将“跳转”回“a”标签。神奇的是“$!”我们在命令之前有的地址前缀。结束括号关闭了该组。该组及其各自的地址前缀允许我们循环遍历所有行,将它们连接在一起,并在最后一行之后停止,允许执行任何进一步的命令。然后我们有最后的脚本:

    #!/bin/sed -f
    
    : a
    $! {
        N
        b a
    }
    
    s,\\,\\\\,g
    s,\n,\\n,g
    s,[][/^$.],\\&,g
    

    如上所述,我们需要转义特殊字符。在这种情况下,实际的换行符现在作为反斜杠后跟字母n进行转义。在最后一个命令中,有更多字符需要以反斜杠为前缀。请注意,要匹配一个结束的方括号,它必须是方括号内的第一个字符,以防止sed将其解释为我们要匹配的字符列表的结束字符。因此,方括号之间按顺序列出的字符为][/^$.

    我们再次执行:sed -f escape_new.sed old > old.tmp

    现在我们可以在sed命令中使用这些转义文件,但我们必须再次将所有行加载到模式空间中。使用与以前相同的命令,但将它们放在一行中我们有紧凑的形式::a;$!{N;ba}:我们现在可以在最终表达式中使用它(没有现在在new.tmp文件上的结束斜杠字符) ):

    find some_files -name '*.html' -exec sed -e ":a;\$!{N;ba};s/`cat old.tmp`/`cat new.tmp`g" -i {} \;
    

    希望它能起作用=)

    请注意,我们使用反斜杠转义了$符号,否则shell会认为我们正在尝试访问$!变量(执行上一个异步命令的结果)。