Как удалить повторяющиеся теги на основе определенного имени атрибута в XML-файле?

Некоторые удаленные файлы могут занимать место на диске/в файловой системе. Запустите приведенную ниже команду от имени привилегированной учетной записи root, чтобы проверить, какие удаленные файлы занимают место

lsof < name_of_filesystem> | grep -i deleted

Как только вы узнаете файлы, выясните, какие службы связаны с этим файлом, и остановите и запустите службу, которая разрешит и освободит место на диске.

Надеюсь, это поможет, но если увидите, пожалуйста, вставьте скриншот ошибки, спасибо.

2
21.05.2021, 11:11
3 ответа

Я не думаю, что 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является то, что basexсначала считывает весь XML-документ в память,поэтому для очень больших файлов, превышающих размер основной памяти, это нецелесообразно. Существуют процессоры XML, которые работают с XML в потоковом режиме , например. В XSLT 3.0 предусмотрены потоковые преобразования, поэтому, если вам нужно обрабатывать огромные файлы, вероятно, есть способ сделать это с помощью любого процессора, поддерживающего XSLT 3.0. Но в этот момент может быть проще просто написать свой собственный небольшой потоковый парсер вручную.

5
28.07.2021, 11:30

Идентифицируйте строку (s )с идентификатором: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

Предупреждение Это изменит порядок содержимого входного файла. Если у вас просто есть список записей без дополнительной структуры, этого решения должно быть достаточно.

1
28.07.2021, 11:30

Предположим, что 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), затем вы можете использовать xq, оболочку синтаксического анализатора XML вокруг jq, из https://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>

Для справки: поскольку xqпостроен на основе jq, выражение фактически применяется к документу JSON, переведенному из вашего XML-документа. Измененный документ JSON затем переводится обратно в XML. Измененный документ JSON с учетом 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"
      },
      {
        "@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"
      }
    ]
  }
}
4
28.07.2021, 11:30

Теги

Похожие вопросы