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 스타, Python lxml또는 이와 유사한 것.

답변2

이것이 어떻게 작동하는지에 대한 기본 요점을 놓치고 있습니다 sed. 한 번에 한 줄씩 입력을 받는다는 점에서 줄 중심 편집기입니다. 그리고 분명히 일치하지 않는 여러 줄 정규식을 처리하도록 요청하고 있습니다.

그렇다면 sed 옵션을 통해 파일을 가져올 GNU sed수 있습니다 . 텍스트 파일에 없는 레코드 구분 기호를 찾습니다 . 따라서 전체 파일을 하나의 긴 레코드로 읽습니다.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 

관련 정보