Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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

share|improve this question
up vote 11 down vote accepted

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>
share|improve this answer
    
Oh wow, I didn't know about xmllint. Thanks! – Nathan Long Sep 16 '11 at 15:42
2  
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! – KernowBunney Dec 23 '13 at 12:02

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)

share|improve this answer

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.

share|improve this answer
    
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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .