如何替换 bash 脚本中的复杂多行文本?

如何替换 bash 脚本中的复杂多行文本?

我想替换 bash 中文件中的特定多行文本,但出现错误。

我认为它失败是因为它是多行的,当我用单行(包含空格和特殊字符)测试代码时它工作正常,但是当我添加完整的多行目标和替换时它失败了。

文件.txt

<html>
    <head>
        <title>
            O-HELLO-1
        </title>
    </head>
</html>

<html>
    <head>
        <title>
            O-HELLO-2
        </title>
    </head>
</html>

脚本文件

#!/bin/bash

target="<html>
    <head>
        <title>
            O-HELLO-1
        </title>
   </head>
</html>";

replacement="<a>
    <b>
        <c>
            R-HELLO-1
        </c>
    </b>
</a>";

echo "------------------";
out=$(sed -i -e "s/$target/$replacement/g" file.txt);

if [[ -n $out ]]; then
    cat file.txt;
    if [ -f file.txt-e ]; then
        rm file.txt-e;
        echo "------------------";
        echo "duplicate file removed.";
    fi
    echo;
fi
echo "------------------";

错误日志

sed: 1: "s/<html>
    <head>
    ...": unterminated substitute pattern

答案1

首先,一句指导的话。该“文本”实际上看起来是一种标记语言,如 XML 或类似语言。从长远来看,将如此复杂和细致入微的输入处理为简单的、无格式的文本很可能会导致问题。我强烈建议使用类似的工具XML小星或类似的代替。

尽管如此,一种解决方案是使用变量,例如 GNU awk 提供的变量:

awk -v target="$target" -v replacement="$replacement" '{ gsub(target, replacement, $0) } 1'

我再次重复:如果您打算重复执行此操作,或者在没有监督结果的情况下,请避免头痛并使用实际处理您正在使用的标记语言的来龙去脉的程序,例如。XML小星、Pythonlxml或类似的。

答案2

你忽略了运作方式的基本点sed。它是一个面向行的编辑器,因为它一次获取一行输入。而您要求它处理多行正则表达式,这显然永远不会匹配。

如果有,GNU sed您可以通过sed 选项将slurp文件输入。-z它查看NUL=\0记录分隔符,在文本文件中找不到该分隔符。因此它会将整个文件作为一条长记录读入。

我们需要调整目标和替换变量,因为它们可能包含被 sed 视为正则表达式的字符。所以我们需要转义它们,然后才能在 sed 表达式中使用它们。

srch=$(printf '%s\n' "$target" |
sed -e '
  H;1h;$!d;x
  s:[][\/^$*.]:\\&:g
  s/[[:space:]]\{1,\}/[[:space:]]\\{1,\\}/g
')

repl=$(printf '%s\n' "$replacement" |
sed -e '
  s:[\&/]:\\&:g
  $!s:$:\\:
')

sed -e '$!{' -e 'N;H;s/.*//;x;D' -e '}' -e "s/$srch/$repl/g" file.txt

结果:

<a>
    <b>
        <c>
            R-HELLO-1
        </c>
    </b>
</a>

<html>
    <head>
        <title>
            O-HELLO-2
        </title>
    </head>
</html>

如果perl您的系统上安装了,那么您也可以使用它;我们匹配相同的骨架,但空白数量不同,因为您不想让单个空格搞乱匹配。

srch="$target"      \
repl="$replacement" \
perl -0777 -pe '
  (my $re = quotemeta $ENV{srch}) =~ s/(\\\s)+/\\s+/g;
  s/$re/$ENV{repl}/g;
' file.txt 

相关内容