Переформатирование большого количества файлов XML

11

Я манипулирую большим количеством XML-файлов, разбросанных по всей структуре вложенных каталогов.

Я попробовал следующее:

$ find . -name "*.xml" -type f | xargs -- xmllint --format

Проблема в том, что генерирует форматированный XML-вывод на экране, но не меняет файл.

Как я могу изменить эту команду, чтобы фактическое содержимое файла изменилось?

Гарри
источник

Ответы:

23

Это может быть сделано с findпомощью -exec:

find . -name "*.xml" -type f -exec xmllint --output '{}' --format '{}' \;

То, что передано, -execбудет вызываться один раз для каждого найденного файла с {}заменой параметров шаблона на текущее имя файла. Команда \;on в конце команды find просто завершает строку.

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

xargsбыло бы необходимо, если бы команда, передаваемая из find, работала с несколькими файлами одновременно, и этот список был длинным. Вы не можете сделать это в этом случае, так как вам нужно передать одно имя файла --outputопции xmllint. Без этого xargsвы можете получить ошибку «Список аргументов слишком длинный», если вы обрабатываете много файлов. xargsтакже поддерживает строки замены файлов с -Iопцией:

find . -name "*.xml" -type f | xargs -I'{}' xmllint --output '{}' --format '{}'

Сделал бы так же, как find -execкоманда выше. Если в любой из ваших папок есть одинаковые символы в одинаковых пробелах, вам нужно будет использовать -0опции findи xargs. Но использование xargsс -Iподразумевает опцию, -L 1которая означает, что обрабатывать только 1 файл за раз, так что вы также можете напрямую использовать findс -exec.

didster
источник
@manatwork спасибо за правки - липкие пальцы; o)
Дидстер
Я только что запустил это, и это, кажется, работает удовольствие! Большое спасибо за быстрый и краткий ответ!
Гарри
2
«Это не удастся, если список файлов слишком велик». Нет, не удастся (он обрабатывает один файл за раз), и на самом деле find … -execэто самый прямой способ сделать это.
Жиль "ТАК - перестать быть злым"
@ Жиль Хорошая мысль! Я обновил свой ответ соответственно.
Дидстер
1
Это работает из-за того, что xmllintсначала загружается полный XML-документ в память, а затем разбирается / записывается. Это позволяет обрабатывать документ на месте.
gavenkoa
6

Я обычно нападаю на эти проблемы со слоем косвенности. Напишите сценарий оболочки, который делает то, что вы хотите, и назовите это. Я бы предложил для начала

#! /bin/sh
for file
do
   xmllint --format $file > $file.tmp && mv $file.tmp $file
done

Попробуйте его на файл или два вручную, затем вы можете заменить его в xargs

find . -name "*.xml" -type f | xargs -- xmltidy.sh
юлианский
источник
Это похоже на хороший подход, если мне нужно будет делать более сложные манипуляции в будущем. Спасибо за ответ.
Гарри