Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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

3 Answers 3

up vote 3 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
1  
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

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.