7

I have a large XML file. From the Unix command line, I'd like to add a newline after every >.

I have tried using sed for this, with no luck:

sed -i '' -e's/>/>\n/' file.xml

This just inserts the letter n, not a newline. I've also tried \r and \r\n.

How can I do this?

(FYI - I'm using zshell in OSX.)

4 Answers 4

17

Script

Use indentxml file.xml to view, indentxml file.xml > new.xml to edit.

Where indentxml is

#!/usr/bin/perl
#
# Purpose: Read an XML file and indent it for ease of reading
# Author:  RedGrittyBrick 2011. 
# Licence: Creative Commons Attribution-ShareAlike 3.0 Unported License
#
use strict;
use warnings;

my $filename = $ARGV[0];
die "Usage: $0 filename\n" unless $filename;

open my $fh , '<', $filename
  or die "Can't read '$filename' because $!\n";
my $xml = '';
while (<$fh>) { $xml .= $_; }
close $fh;

$xml =~ s|>[\n\s]+<|><|gs;                       # remove superfluous whitespace
$xml =~ s|><|>\n<|gs;                            # split line at consecutive tags

my $indent = 0;
for my $line (split /\n/, $xml) {

  if ($line =~ m|^</|) { $indent--; }

  print '  'x$indent, $line, "\n";

  if ($line =~ m|^<[^/\?]|) { $indent++; }             # indent after <foo
  if ($line =~ m|^<[^/][^>]*>[^<]*</|) { $indent--; }  # but not <foo>..</foo>
  if ($line =~ m|^<[^/][^>]*/>|) { $indent--; }        # and not <foo/>

}

Parser

Of course, the canonical answer is to use a proper XML parser.

# cat line.xml
<a><b>Bee</b><c>Sea</c><d><e>Eeeh!</e></d></a>

# perl -MXML::LibXML -e 'print XML::LibXML->new->parse_file("line.xml")->toString(1)'
<?xml version="1.0"?>
<a>
  <b>Bee</b>
  <c>Sea</c>
  <d>
    <e>Eeeh!</e>
  </d>
</a>

Utility

But maybe the easiest is

# xmllint --format line.xml
<?xml version="1.0"?>
<a>
  <b>Bee</b>
  <c>Sea</c>
  <d>
    <e>Eeeh!</e>
  </d>
</a>
3
  • Oh wow, I didn't know about xmllint. Thanks! Sep 16, 2011 at 15:42
  • 3
    Of course, as usual, any attempt parsing XML with regexps are futile. Never forget <something attribute="<foo>"> is valid XML, string type attributes may take any literal string as a value. w3.org/TR/xml/#sec-attribute-types
    – chx
    Sep 17, 2011 at 8:10
  • +1 for alerting me to the existence of xmllint! That solved my issue! Thanks!
    – ccbunney
    Dec 23, 2013 at 12:02
2

There is no escape sequence, you need to literally use the newline character. So for this input

$ cat /tmp/example 
<this is one tag><this is another tag><here again>

You would have to use

$ sed -e 's_>_&\
_g' /tmp/example

which produces

<this is one tag>
<this is another tag>
<here again>

Note that the newline has to be escaped (as shown above)

0

Your command works properly, just not enough.

Try adding the 'g' option to the end of the 's'ubstitute command to make sed examine ALL of the '>' characters in each line of the input file.

Use:

sed -i -e 's/>/>\n/g' file.xml

note the trailing 'g' on the substitute command.

the suffix part of the '-i' option is optional, and may be omitted.

The other answers given work nicely too, but your initial attempt was correct, albeit missing the 'g'lobal option.

4
  • On my OSX machine using zshell, that command converts <foo><bar>stuff</bar></foo> to <foo>n<bar>nstuff</bar>n</foo>n. Sep 19, 2011 at 15:08
  • Hmmm, that means zsh is screwing with escaped characters within a SINGLE quoted string. That's not good. Single quotes are supposed to be untouched by interpretation by the shell. I have not been able to reproduce this output myself, using your original command, perhaps something from your zsh environment is affecting quoted strings. I'd suggest looking at any startup scripts / customizations you've applied to your zsh shell.
    – lornix
    Sep 21, 2011 at 23:39
  • After reading your comment, I tried this with both single and double quotes from zsh, then I switched to bash and tried both ways. In every case, it just inserted n. I'm using OSX Snow Leopard - what are you using? Sep 22, 2011 at 12:10
  • Oh, that's likely the big issue, I'm using linux, you've got the OSX tweaked sed. hmm. Stupid things to try: double the backslash (\\n), try \r (or \\r) for mac style line endings (shouldn't matter, \n is universal). Try it with perl: perl -i -e 's/>/>\n/g' xxx.xml Perl might work, pretty much the same cmd line options. can do multiple files if you add the '-p' option too. Frustrating that a low-level app like sed doesn't operate as expected though. Maybe there's an option to turn off 'pooh-headed tweaks because we can' or something.
    – lornix
    Sep 23, 2011 at 10:53
-1

Why don't you use xmllint --format which will reformat exactly that way.

1
  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Feb 4 at 19:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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