sed를 사용하여 복잡한 문자열 찾기 및 바꾸기 (바람직하게 정규식으로)


84

다음 내용의 파일이 있습니다.

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

첫 번째 줄의 "name"을 "something"으로 바꾸고 두 번째 줄의 "password"를 "somethingelse"로 바꾸고 세 번째 줄의 "name"을 "somethingdifferent"로 바꾸는 스크립트를 만들어야합니다. 파일에서 발생하는 순서에 의존 할 수 없으므로 "name"의 첫 번째 항목을 "something"으로 바꾸고 "name"의 두 번째 항목을 "somethingdifferent"로 간단히 바꿀 수 없습니다. 실제로 올바른 문자열을 찾아 바꾸려면 주변 문자열을 검색해야합니다.

지금까지 첫 번째 "이름"어커런스를 찾아서 바꾸기 위해이 명령을 시도했습니다.

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

그러나 그것은 작동하지 않으므로이 문자 중 일부는 탈출해야 할 수도 있습니다.

이상적으로는 정규식을 사용하여 두 "사용자 이름"어커런스를 일치시키고 "이름"만 바꿀 수 있기를 바랍니다. 이 같은하지만 sed:

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

괄호 안의 내용을 "무언가"로 바꿉니다.

이게 가능해?


2
정규 표현식 기반 솔루션은 매우 많이 고려되지 않는 한 입력 형식이 변경 될 때마다 중단 될 위험이 있습니다. 정규 표현식은 XML, SGML 또는 파생물 (이것이 나에게 보이는)을 처리하기에 좋지 않은 선택입니다.
CVn

승인! XQuery 사용 (예 : w3schools.com/xquery/default.asp) 을 고려하십시오 . 이것은 XML 컨텐츠를 검색하고 조작하기위한 W3C 표준입니다.
lgeorget 2016 년

답변:


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

이것은 당신이 찾고있는 것입니다.

설명:

  • 첫 번째 부분의 괄호는 두 번째 부분에서 재사용 할 수있는 그룹 (사실 문자열)을 정의합니다.
  • \1, \2등 번째 부분의 첫 번째 부분에서 캡처 i 번째 그룹에 대한 참조는 (번호 매기기는 1로 시작)
  • -E확장 정규식 (필요 +하고 그룹화) 을 활성화 합니다.

20
-E 옵션의 경우 +1
slackmart

4
이름이 백업 파일 뒤에 남습니다 (original name) + "-E".
Sarge Borsch 5

4
OSX에서는 'sed : 1 : "s / (<username>. +) name (. + ...": \ 1 RE에 정의되지 않았습니다.'라는 메시지가 표시됩니다.이 질문의 정확한 예를 파일에 붙여 넣었습니다. 이 파일에서이 답변의 명령을 실행했습니다
.OSX에

1
sed의 gnu 버전은 "-E"매개 변수를 지원하지만 공식은 지원하지 않습니다. 맨 페이지에는 언급되지 않았습니다. 확장 정규식을 사용하려면 "-r"매개 변수를 대신 사용해야합니다.
Ikem Krueger

3
@deweydb에 따르면 이 답변 , 당신은 사용해야 \(하고 \)대신 ().
Zhang Buzz

14
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/는 전에 s문자열 '이름'을 포함하는 라인에만 작업에 나오지도 알려줍니다.


1
우아하고 효율적이며 케이스에 완벽하게 맞습니다. +1
lgeorget 2016 년

6

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

파일이 유효한 XML (3 개의 XML 모양 태그가 아닌) 인 경우 XMLStarlet 을 사용할 수 있습니다 .

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>

3

명령 \[.*^$/의 정규식 부분 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~'

3
$ sed -e '1s/name/something/2' \
      -e '3s/name/somethingdifferent/2' \
      -e 's/password/somethingelse/2' sample.xml

행 번호를 나타내는 "s"앞의 숫자와 같이 주소를 간단히 사용할 수 있습니다.

또한 마지막 숫자 sed는 첫 번째 일치 항목을 바꾸는 대신 두 번째 일치 항목을 바꾸 라고 지시 합니다.


1

"name"단어를 "something"단어로 바꾸려면 다음을 사용하십시오.

sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml

그것은 지정된 단어의 모든 발생을 대체 할 것입니다.

지금까지 모든 것이 표준 출력으로 출력됩니다.

sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml > anotherfile.xml

다른 파일에 변경 사항을 저장합니다.


0
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...

    -r, --regexp-extended
             use extended regular expressions in the script.

속성 파일의 값을 바꾸려면

sed -i -r 's/MAIL\=(.+)/MAIL\=user@mymail.com/' etc/service.properties 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.