sed(정규식 선호)를 사용하여 복잡한 문자열 찾기 및 바꾸기

sed(정규식 선호)를 사용하여 복잡한 문자열 찾기 및 바꾸기

다음 내용이 포함된 파일이 있습니다.

<username><![CDATA[name]]></username>
<password><![CDATA[password]]></password>
<dbname><![CDATA[name]]></dbname>

첫 번째 줄의 "이름"을 "something"으로, 두 번째 줄의 "password"를 "somethingelse"로, 세 번째 줄의 "name"을 "something other"로 변경하는 스크립트를 만들어야 합니다. 파일의 발생 순서에 의존할 수 없기 때문에 "이름"의 첫 번째 발생을 "무언가"로 바꾸고 두 번째 "이름" 발생을 "무언가 다른 것"으로 바꿀 수는 없습니다. 실제로 올바른 콘텐츠를 찾아서 교체했는지 확인하기 위해 주변 문자열을 검색해야 합니다.

지금까지 나는 이 명령을 사용하여 "이름"의 첫 번째 항목을 찾아서 바꾸려고 했습니다.

sed -i "s/<username><![CDATA[name]]><\/username>/something/g" file.xml

하지만 작동하지 않으므로 일부 문자를 이스케이프 처리해야 할 수도 있습니다.

이상적으로는 정규식을 사용하여 두 개의 "사용자 이름"을 일치시키고 "이름"만 바꿀 수 있기를 바랍니다. 이와 비슷하지만 다음을 사용합니다 sed.

<username>.+?(name).+?</username>

그리고 괄호 안의 내용을 "something"으로 바꿉니다.

가능합니까?

답변1

sed -i -E "s/(<username>.+)name(.+<\/username>)/\1something\2/" file.xml

나는 이것이 당신이 찾고 있는 것이라고 생각합니다.

설명하다:

  • 첫 번째 부분의 대괄호는 두 번째 부분에서 재사용할 수 있는 그룹(실제로는 문자열)을 정의합니다.
  • \1두 번째 부분의 등은 \2첫 번째 부분에서 캡처된 i번째 그룹에 대한 참조입니다(번호는 1부터 시작).
  • -E+확장 정규식을 활성화합니다(그룹화에 필요).
  • -i"내부" 파일 편집 모드 활성화

답변2

sed -e '/username/s/CDATA\[name\]/CDATA\[something\]/' \
-e '/password/s/CDATA\[password\]/CDATA\[somethingelse\]/' \
-e '/dbname/s/CDATA\[name\]/CDATA\[somethingdifferent\]/' file.txt

이전 /username/예에서는 ssed에게 "username"이라는 문자열이 포함된 행에서만 작업하도록 지시했습니다.

답변3

어려운 요구 사항이 아닌 경우 sed특수 도구를 사용하는 것이 가장 좋습니다.

파일이 유효한 XML(XML처럼 보이는 3개의 태그뿐만 아니라)인 경우 다음을 사용할 수 있습니다.XML 스타:

xml ed -P -O -L \
  -u '//username/text()' -v 'something' \
  -u '//password/text()' -v 'somethingelse' \
  -u '//dbname/text()' -v 'somethingdifferent' file.xml

위의 방법은 정규식을 해결하기 어려운 상황에도 적합합니다.

  • 현재 값을 지정하지 않고 레이블 값을 바꿀 수 있습니다.
  • 이러한 값은 단순히 이스케이프되어 CDATA에 포함되지 않은 경우에도 대체될 수 있습니다.
  • 태그에 속성이 있어도 값은 대체될 ​​수 있습니다.
  • 동일한 이름의 태그가 여러 개 존재할 경우 나타나는 태그만 쉽게 교체할 수 있습니다.
  • 수정된 XML은 들여쓰기를 통해 형식을 지정할 수 있습니다.

위의 간단한 데모:

bash-4.2$ cat file.xml
<sith>
<master>
<username><![CDATA[name]]></username>
</master>
<apprentice>
<username><![CDATA[name]]></username>
<password>password</password>
<dbname foo="bar"><![CDATA[name]]></dbname>
</apprentice>
</sith>

bash-4.2$ xml ed -O -u '//apprentice/username/text()' -v 'something' -u '//password/text()' -v 'somethingelse' -u '//dbname/text()' -v 'somethingdifferent' file.xml
<sith>
  <master>
    <username><![CDATA[name]]></username>
  </master>
  <apprentice>
    <username><![CDATA[something]]></username>
    <password>somethingelse</password>
    <dbname foo="bar"><![CDATA[somethingdifferent]]></dbname>
  </apprentice>
</sith>

답변4

\[.*^$/정규식 부분 s\&/명령의 대체 부분을 모두 개행 문자로 인용 해야 합니다 . 정규 표현식은기본 정규식, 또한 s명령의 구분 기호를 인용해야 합니다.

인용을 피하기 위해 다른 구분 기호를 선택할 수 있습니다 /. 대신 문자를 인용해야 하지만 일반적으로 구분 기호를 변경하는 목적은 바꾸려는 텍스트를 선택하거나 텍스트에 표시되지 않는 구분 기호를 바꾸는 것입니다.

sed -e 's~<username><!\[CDATA\[name\]\]></username>~<username><![CDATA[something]]></username>~'

그룹을 사용하면 텍스트 부분을 반복적으로 바꾸는 것을 방지하고 해당 부분의 변경 사항을 수용할 수 있습니다.

sed -e 's~\(<username><!\[[A-Z]*\[\)name\(\]\]></username>\)~\1something\2~'

sed -e 's~\(<username>.*[^A-Za-z]\[\)name\([^A-Za-z].*</username>\)~\1something\2~'

관련 정보