Git Bash를 사용하여 수백 개의 파일에 있는 yrot 태그의 내용을 조건부로 바꾸려고 합니다. 단, 해당 내용이 바퀴 관련 부품 이름 태그에 속하는 경우에만 가능합니다.
// YES, change
<part name="D_wheel1" seqNumber="1" >
<yrot min="0.000000" max="0.000000" cur="0.000000" />
</part>
// YES, change
<part name="D_wheel2" seqNumber="1" >
<yrot min="0.000000" max="0.000000" cur="0.000000" />
</part>
// NO, don't change
<part name="door" seqNumber="1" >
<yrot min="0.000000" max="0.000000" cur="0.000000" />
</part>
// Example Line Change
// From: <yrot min="0.000000" max="0.000000" cur="0.000000" />
// To: <yrot min="INF" max="INF"/>
awk와 같은 도구를 사용하면 이것이 가능합니까? 아니면 특별한 XML 파서를 사용해야 합니까?
편집: 명확하게 말하면 에 속하는 태그가 약 12개 있으며 그 중 하나는 태그 내에만 나타납니다. 이름에 "wheel"이 포함된 경우에만 줄을 바꾸고 싶습니다. 그 자체가 중첩되어 있습니다.
XML 파서가 필요하다고 주장하는 사람들의 경우 조건이 충족되면(yrot 태그가 휠에 있음) 간단한 텍스트 찾기/바꾸기가 작동하지 않는 이유는 무엇입니까? 확인이 그렇게 어렵나요?
답변1
XML을 다음 data.xml
과 같이 제공하십시오.
$ cat data.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<part name="D_wheel1" seqNumber="1">
<yrot min="0.000000" max="0.000000" cur="0.000000" />
</part>
<part name="D_wheel2" seqNumber="1">
<yrot min="0.000000" max="0.000000" cur="0.000000" />
</part>
<part name="door" seqNumber="1">
<yrot min="0.000000" max="0.000000" cur="0.000000" />
</part>
</root>
xmlstarlet
그리고 사용X 경로:
$ xmlstarlet ed \
--var target '//part[contains(@name, "wheel")]/yrot' \
-u '$target/@*[name()="min" or name()="max"]' -v 'INF' \
-d '$target/@cur' data.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<part name="D_wheel1" seqNumber="1">
<yrot min="INF" max="INF"/>
</part>
<part name="D_wheel2" seqNumber="1">
<yrot min="INF" max="INF"/>
</part>
<part name="door" seqNumber="1">
<yrot min="0.000000" max="0.000000" cur="0.000000"/>
</part>
</root>
아니면 고전적인 방법을 사용하세요XSLT: 및 xsltproc
/또는xmlstarlet
$ cat data.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[contains(@name, 'wheel')]/yrot">
<xsl:copy>
<xsl:attribute name="min">INF</xsl:attribute>
<xsl:attribute name="max">INF</xsl:attribute>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
$ xsltproc data.xsl data.xml #or: xmlstarlet tr data.xsl data.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<part name="D_wheel1" seqNumber="1">
<yrot min="INF" max="INF"/>
</part>
<part name="D_wheel2" seqNumber="1">
<yrot min="INF" max="INF"/>
</part>
<part name="door" seqNumber="1">
<yrot min="0.000000" max="0.000000" cur="0.000000"/>
</part>
</root>
답변2
Python의 ElementTree 표준 라이브러리 사용:
#! /usr/bin/env python
import sys
import xml.etree.ElementTree as ET
def do_one(file_name):
tree = ET.parse(file_name)
for part in tree.findall("part"):
if not 'wheel' in part.attrib['name']:
continue
for yrot in part.findall('yrot'):
names = []
for x in yrot.attrib:
names.append(x)
for x in names:
del yrot.attrib[x]
yrot.attrib['min'] = 'INF'
yrot.attrib['max'] = 'INF'
tree.write(file_name)
for file_name in sys.argv[1:]:
do_one(file_name)
그러면 명령줄에서 스크립트에 전달된 모든 파일이 구문 분석됩니다.
python convert_xml.py *.xml
답변3
"표준" 유닉스 도구를 사용하여 XML을 구문 분석하는 데에는 큰 문제가 있습니다. XML은 의미상 동일하지만 동일한 줄과 들여쓰기가 없는 여러 레이아웃을 지원하는 데이터 구조입니다.
이는 라인 기반/정규식 기반 구문 분석이 근본적으로 깨지기 쉬운 코드를 생성하게 되므로 정말 나쁜 생각이라는 것을 의미합니다. 누군가가 어느 시점에서 XML을 재구성할 수 있으며 코드는 뚜렷한 이유 없이 중단될 수 있습니다. 이런 종류의 일은 유지 관리 프로그래머와 미래의 시스템 관리자에게 상당한 고통을 안겨줄 수 있습니다.
그렇습니다. XML 파서를 사용하십시오. 여러 가지 옵션이 있습니다. 누군가가 Python 옵션을 제공했기 때문에 여기에도 Perl을 포함시켰습니다.
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
sub process_part {
my ( $twig, $part ) = @_;
if ( $part->att('name') =~ m/wheel/ ) {
$part->first_child('yrot')->set_att( 'min', 'INF' );
$part->first_child('yrot')->set_att( 'max', 'INF' );
}
}
my $twig = XML::Twig->new(
'pretty_print' => 'indented_a',
'twig_handlers' => { 'part' => \&process_part }
);
$twig->parsefile('your_file.xml');
$twig->print;
이제 텍스트를 "검사"하는 것이 왜 어려운지에 대해서는 다음과 같습니다.
<root>
<part
name="D_wheel1"
seqNumber="1">
<yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/>
</part>
<part
name="D_wheel2"
seqNumber="1">
<yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/>
</part>
<part
name="door"
seqNumber="1">
<yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/>
</part>
</root>
그리고:
<root><part name="D_wheel1" seqNumber="1"><yrot cur="0.000000" max="0.000000" min="0.000000"/></part><part name="D_wheel2" seqNumber="1"><yrot cur="0.000000" max="0.000000" min="0.000000"/></part><part name="door" seqNumber="1"><yrot cur="0.000000" max="0.000000" min="0.000000"/></part></root>
그리고:
<root
><part
name="D_wheel1"
seqNumber="1"
><yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/></part><part
name="D_wheel2"
seqNumber="1"
><yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/></part><part
name="door"
seqNumber="1"
><yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/></part></root>
의미상 모두 동일하지만 보시다시피 동일한 내용이 구문 분석되지 않기를 바랍니다. 단항 태그와 같은 것 - 예: >
<yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/>
비교:
<yrot cur="0.000000" max="0.000000" min="0.000000" ></yrot>
그리고 - 의미는 동일합니다. 그래서 당신은할 수 있는줄과 정규식에서 벗어나십시오. 그러나 이는 도박이며 깨지기 쉬운 코드를 작성합니다.
답변4
awk를 사용하세요. 이는 표시된 대로 매우 간단한 파일 구조를 가정합니다. XLM 파일에서 작동한다고 보장할 수 없습니다. 사실, 그렇지 않다고 단언할 수 있습니다.
awk '{if(/<\/part>/){p=0}if($1~/<part/ && $2~/wheel/){p=1}
if(p==1 && /<yrot/){
print "<yrot min=\"INF\" max=\"INF\"/>"
} else{print}}' file
하지만 엄밀히 말하면 매우 취약합니다. name=
필드를 구분하는 줄의 두 번째 공백은 항상 중첩된 태그 및 기타 다양한 합병증이 있는 경우 중단된다고 가정합니다 . 제공한 예제에서 원하는 출력을 제공하지만 파일을 아주 조금만 변경하면 중단됩니다. 적절한 파서를 사용하는 Anthon의 접근 방식은 훨씬 안전합니다.