태그의 텍스트를 변경합니다(태그가 XML 블록 내에 포함된 경우에만 해당).

태그의 텍스트를 변경합니다(태그가 XML 블록 내에 포함된 경우에만 해당).

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의 접근 방식은 훨씬 안전합니다.

관련 정보