bashスクリプトで複雑な複数行テキストを置き換える方法は?

bashスクリプトで複雑な複数行テキストを置き換える方法は?

Bashのファイルで特定の複数行のテキストを置き換えたいのですが、エラーが発生します。

複数行なので失敗だと思います。 1行(スペースと特殊文字を含む)でコードをテストすると正常に動作しますが、複数行のターゲット全体を追加して置き換えると失敗します。

ファイル.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スターまたは代わりに同様のもの。

それにもかかわらず、解決策の1つは、GNU awkが提供するものと同じ変数を使用することです。

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

もう一度繰り返します。この操作を繰り返し実行したり、結果を監視したくない場合は、気になることを避け、実際に使用しているマークアップ言語のすべての部分を処理するプログラムを使用してください。XMLスター、Pythonlxmlまたは同様のもの。

答え2

これがどのように機能するかについての基本的なポイントを見逃していますsed。一度に1行ずつ入力を受け取るという点で、行中心エディタです。そして、明らかに一致しない複数行の正規表現を処理するように求めています。

その場合は、sedオプションを使用してファイルをインポートGNU sedできます。テキストファイルにないレコード区切り文字を見つけます。したがって、ファイル全体を1つの長いレコードとして読み込みます。slurp-zNUL=\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 

関連情報