Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I'm manipulating a large number of XML files scattered throughout a nested directory structure. I tried the following (which almost works):

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

The problem is this generates the formatted XML output on the screen, but doesn't change the file. How can I change this command so that the actual file contents are changed? Thanks!

share|improve this question

2 Answers 2

up vote 7 down vote accepted

This can be done from find directly using -exec:

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

Whats passed to -exec will be invoked once per file found with the template parameters {} being replaced with the current file name. The \; on the end of the find command just terminates the line.

The use of xargs isn't really necessary in this case because we need to invoke xmllint once per file as both the input and output file names must be specified within the same call.

xargs would be needed if the command being piped to from find was working on multiple files at a time and that list was long. You cant do that in this case as you need to pass the single filename to the --output option of xmllint. Without xargs you could end up with a "Argument List to long" error if you are processing a lot of files. xargs also supports file replace strings with the -I option:

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

Would do the same as the find -exec command above. If any of your folders have odd chars in like spaces you will need to use the -0 options of find and xargs. But using xargs with -I implies the option -L 1 which means only process 1 file at a time anyway, so you may as well directly use find with -exec.

share|improve this answer
    
@manatwork thanks for the edits - sticky fingers ;o) –  didster Oct 8 '12 at 7:46
    
I just ran this and it appears to work a treat! Many thanks for the prompt and concise response! –  Harry Oct 8 '12 at 8:03
    
“This will fail if the file list is too large”: No, it won't fail (it's processing a single file at a time), and in fact find … -exec is the most direct way to do this. –  Gilles Oct 8 '12 at 23:02
    
@Gilles Good point! I have updated my answer accordingly. –  didster Oct 11 '12 at 7:31

I typically attack these problems with a layer of indirection. Write a shell script that does what you want, and call that. I'd suggest as a start

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

The try it out on a file or two by hand, then you can replace it in the xargs

find . -name "*.xml" -type f | xargs -- xmltidy.sh
share|improve this answer
    
This looks like a good approach if I should need to do more complex manipulation in the future. Thanks for the response. –  Harry Oct 8 '12 at 8:05

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.