XML 파일의 특정 속성 이름을 기반으로 중복 태그를 제거하는 방법은 무엇입니까?

XML 파일의 특정 속성 이름을 기반으로 중복 태그를 제거하는 방법은 무엇입니까?

"groupName"을 기반으로 중복 행을 제거하고 행을 유지하는 방법은 무엇입니까 directoryId="1"?

<Group id="123" groupName="ABC" lowerGroupName="abc" active="1" local="1" createdDate="2017-08-21 09:28:30.581" updatedDate="2017-08-21 09:28:30.581" type="GROUP" directoryId="10100"/>
<Group id="456" groupName="ABC" lowerGroupName="abc" active="1" local="0" createdDate="2017-08-21 09:28:30.634" updatedDate="2017-08-21 09:28:30.634" type="GROUP" directoryId="1"/>

답변1

나는 uniq이것이 공백으로 구분된 파일이나 고정 너비 파일(단 두 개의 "열" 관련 옵션이 있고 --skip-fields표시되는 파일 --skip-chars)에서 작동하기 때문에 반드시 올바른 도구라고 생각하지 않습니다. 반면 여기에 있는 것은 XML과 유사한 열입니다. 너비 데이터에는 고정된 구분 기호나 단순한 단일 문자 구분 기호가 없습니다( groupName원칙적으로 등의 값에는 공백이 포함될 수 있습니다).

대신 XML 처리 도구를 사용하겠습니다.

자신만의 스크립트를 작성하지 않는 한 가지 옵션은 XPath 기반 필터링입니다. 다음 답변에서 XPath를 사용하여 고유성을 필터링하는 방법을 배울 수 있습니다.이것들- 중요한 문법요소는 following-sibling::합축 입니다 preceding-sibling::. XPath 표현식을 평가하기 위한 명령줄 도구는 다음 질문에 대한 답변에서 찾을 수 있습니다.이 문제. 내가 시도한 것 중 설치가 가장 쉬운 것은 basex(권장)여기) 그래서 아래에서 사용하겠습니다.

귀하의 질문을 올바르게 이해했다면 동일한 행(XML 요소)이 있는 행을 마지막 행으로 줄이고 싶습니다 groupName(또는 행이 있는 행을 선택하는 다른 이유가 있습니까 directoryId="1"?). 다음과 같은 XML 문서의 경우:

<Groups>
<Group id="123" groupName="ABC" lowerGroupName="abc" active="1" local="1" createdDate="2017-08-21 09:28:30.581" updatedDate="2017-08-21 09:28:30.581" type="GROUP" directoryId="10100"/>
<Group id="456" groupName="ABC" lowerGroupName="abc" active="1" local="0" createdDate="2017-08-21 09:28:30.634" updatedDate="2017-08-21 09:28:30.634" type="GROUP" directoryId="1"/>
<Groups>

Groups이를 달성하려면 루트 요소( )의 모든 항목을 래핑해야 합니다.올바른 형식의 XML, 이 요구 사항은 다음 XPath 표현식을 통해 달성할 수 있습니다.

/Groups/Group[not(@groupName = following-sibling::Group/@groupName)]

/Groups/Group반환할 요소를 선택하고 의 표현식을 사용하여 필터링합니다 []. @속성을 선택하고 following-sibling::현재 속성의 모든 후속 형제 항목과 일치합니다(참조:여기).

이를 실행하면 basex예상되는 결과가 생성됩니다.

$ basex -i - '/Groups/Group[not(@groupName = following-sibling::Group/@groupName)]'

# [paste this into the terminal:]

<Groups>
<Group id="123" groupName="ABC" lowerGroupName="abc" active="1" local="1" createdDate="2017-08-21 09:28:30.581" updatedDate="2017-08-21 09:28:30.581" type="GROUP" directoryId="10100"/>
<Group id="456" groupName="ABC" lowerGroupName="abc" active="1" local="0" createdDate="2017-08-21 09:28:30.634" updatedDate="2017-08-21 09:28:30.634" type="GROUP" directoryId="1"/>
</Groups>

# [output:]

<Group id="456" groupName="ABC" lowerGroupName="abc" active="1" local="0" createdDate="2017-08-21 09:28:30.634" updatedDate="2017-08-21 09:28:30.634" type="GROUP" directoryId="1"/>

이와 대조적으로 단점은 uniq전체 basexXML 문서가 먼저 메모리로 읽혀지기 때문에 주 메모리 크기를 초과하는 매우 큰 파일에는 적합하지 않다는 것입니다. 일부 XML 프로세서가 있습니다.스트리밍 방식으로 XML 조작, 예를 들어 XSLT 3.0에는 스트리밍 변환이 있으므로 대용량 파일을 처리해야 하는 경우 XSLT 3.0을 지원하는 프로세서를 사용하여 처리하는 방법이 있을 수 있습니다. 하지만 그때까지는 자신만의 작은 스트림 파서를 수동으로 작성하는 것이 더 쉬울 수도 있습니다.

답변2

XML 문서가 잘 구성되어 있다고 가정합니다.

<Groups>
<Group id="123" groupName="ABC" lowerGroupName="abc" active="1" local="1" createdDate="2017-08-21 09:28:30.581" updatedDate="2017-08-21 09:28:30.581" type="GROUP" directoryId="10100"/>
<Group id="456" groupName="ABC" lowerGroupName="abc" active="1" local="0" createdDate="2017-08-21 09:28:30.634" updatedDate="2017-08-21 09:28:30.634" type="GROUP" directoryId="1"/>
</Groups>

(방금 이라는 루트 노드를 추가했습니다. ) 다음 에서 XML 파서 래퍼를 Groups사용할 수 있습니다 .xqjqhttps://kislyuk.github.io/yq/,이와 같이:

xq -x '.[].Group |= unique_by(."@groupName")' file.xml

Group이는 속성을 기반으로 고유한 노드만 유지합니다 groupName. 표시된 첫 번째 속성 값 노드가 유지됩니다.

위 명령을 최상위 XML에 적용한 결과는 다음과 같습니다.

<Groups>
  <Group id="123" groupName="ABC" lowerGroupName="abc" active="1" local="1" createdDate="2017-08-21 09:28:30.581" updatedDate="2017-08-21 09:28:30.581" type="GROUP" directoryId="10100"></Group>
</Groups>

가장 낮은 속성 값을 가진 노드를 얻으려면 directoryID목록을 고유화하기 전에 해당 값으로 노드를 정렬하십시오.

xq -x '.[].Group |= (sort_by(."@directoryId") | unique_by(."@groupName"))' file.xml

이로 인해

<Groups>
  <Group id="456" groupName="ABC" lowerGroupName="abc" active="1" local="0" createdDate="2017-08-21 09:28:30.634" updatedDate="2017-08-21 09:28:30.634" type="GROUP" directoryId="1"></Group>
</Groups>

참고로 이 표현식은 xqtop 을 기반으로 작성되었기 때문에 jq실제로 XML 문서를 번역한 JSON 문서에 적용됩니다. 수정된 JSON 문서는 다시 XML로 변환됩니다. 이 답변 상단의 XML을 고려하면 수정된 JSON 문서는 다음과 같습니다.

{
  "Groups": {
    "Group": [
      {
        "@id": "123",
        "@groupName": "ABC",
        "@lowerGroupName": "abc",
        "@active": "1",
        "@local": "1",
        "@createdDate": "2017-08-21 09:28:30.581",
        "@updatedDate": "2017-08-21 09:28:30.581",
        "@type": "GROUP",
        "@directoryId": "10100"
      },
      {
        "@id": "456",
        "@groupName": "ABC",
        "@lowerGroupName": "abc",
        "@active": "1",
        "@local": "0",
        "@createdDate": "2017-08-21 09:28:30.634",
        "@updatedDate": "2017-08-21 09:28:30.634",
        "@type": "GROUP",
        "@directoryId": "1"
      }
    ]
  }
}

답변3

식별자를 사용하여 행을 식별합니다.grep 'groupName="ABC"'

제외 기준을 선택 취소하려는 특정 행:grep -v 'directoryId="1"'

삭제하려는 줄이 표시됩니다. 이제 중복 행을 강제로 적용하고 구체적으로 제거할 수 있습니다.

grep 'groupName="ABC"' input-file | grep -v 'directoryId="1"' > to-remove
cat input-file to-remove | sort | uniq -u > output-file

마지막에 모든 것을 정리하려면 다음을 추가할 수 있습니다.

rm to-remove input-file
mv output-file input-file

경고하다그러면 입력 파일의 내용이 재정렬됩니다. 항목 목록만 있고 다른 구조는 없다면 이 솔루션으로 충분합니다.

답변4

다른 대답은 데이터의 XML 특성을 무시하지만 a) 이것이 생산 작업 흐름이 아닌 "일회성"으로 사용될 것이라는 가정 하에서만 유효합니다. b) 각 행의 속성은 정확히 동일한 순서 c) 이전의 속성 다음에는 공백이 있는 줄이 없습니다 (또는 해당 값 groupName내에도 ).groupName

이 답변awk공백으로 구분된 필드를 기반으로 중복 항목을 필터링하는 방법을 보여줍니다 . 귀하의 경우에는 섹션이 공백으로 구분된 세 번째 열이기 awk '!seen[$3]++'때문에 마찬가지입니다 . groupName그러나 내가 올바르게 이해했다면 당신은마지막awk첫 번째 행이 아닌 각 "반복 그룹"에 대한 행입니다( 위 행에서 제공하는 내용입니다 ). 이를 달성하려면 tac에 입력하기 전에 줄의 순서를 간단히 뒤집은 awk다음 다시 뒤집어 원래 순서를 복원하면 됩니다.

$ tac | awk '!seen[$3]++' | tac

# [paste this into the terminal:]

<Group id="123" groupName="ABC" lowerGroupName="abc" active="1" local="1" createdDate="2017-08-21 09:28:30.581" updatedDate="2017-08-21 09:28:30.581" type="GROUP" directoryId="10100"/>
<Group id="456" groupName="ABC" lowerGroupName="abc" active="1" local="0" createdDate="2017-08-21 09:28:30.634" updatedDate="2017-08-21 09:28:30.634" type="GROUP" directoryId="1"/>

# [output is:]

<Group id="456" groupName="ABC" lowerGroupName="abc" active="1" local="0" createdDate="2017-08-21 09:28:30.634" updatedDate="2017-08-21 09:28:30.634" type="GROUP" directoryId="1"/>

관련 정보