5

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.)

15

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>
  • Oh wow, I didn't know about xmllint. Thanks! – Nathan Long Sep 16 '11 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 '11 at 8:10
  • +1 for alerting me to the existence of xmllint! That solved my issue! Thanks! – ccbunney Dec 23 '13 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.

  • On my OSX machine using zshell, that command converts <foo><bar>stuff</bar></foo> to <foo>n<bar>nstuff</bar>n</foo>n. – Nathan Long Sep 19 '11 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 '11 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? – Nathan Long Sep 22 '11 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 '11 at 10:53

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.