다음 내용이 포함된 파일이 있습니다.
<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/
예에서는 s
sed에게 "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~'