11

I've the following format of the file:

<common>
fitnes=0
genetic=1
method=0
</common>
<inputs>
foo=bar
bar=foo
</inputs>
<limits>
balance=200.00
</limits>

and I would like to delete everything what is between <inputs> and </inputs> (excluding pattern it-self) and replace it with the content from another file (e.g. foo.txt). In other words lines with foo=bar and bar=foo would be replaced with another content.

Probably it can be similar to how you delete a multi-line match, like:

:g/<inputs/,/inputs>/d

but I'm not sure with what I should replace the d in order to insert the content of another file, but I want to keep the matching pattern.

Similar approach would be to remove inner content of html tag, like

:/<inputs>/norm vitd

but then I don't know how would you add into it the content from the file.

Ideally I'm trying to find one liner, since it'll be part of another script.

How can I achieve that?

0

5 Answers 5

12

A substitution can be used to replace a pattern with the result of an expression like this:

:keeppatterns %s;<inputs>\zs\_.\{-}\ze</inputs>;\=insert(readfile('test.txt'), '')

See :help sub-replace-expression

Note that keeppatterns prevents the substitute command from adding anything to the search history and preserves the "last search pattern register" - see :help "/. It's good practice to prevent scripts from unnecessarily modifying Vim's global state with the command modifiers keepalt, keepjumps, lockmarks, keepmarks, and keeppatterns.

0
6

This seems to work, at least on a Unix system:

:/<inputs>/+1,/<\/inputs>/-1!cat foo.txt

It uses the {range}!{filter} command to filter the lines from one after <inputs> to one before </inputs> through the external program cat foo.txt. In this case, cat ignores its standard input, so the original lines are deleted and replaced by the contents of the file foo.txt.

See:

:help :range!
:help cmdline-ranges
5

An alternative approach to that from Gary is this:

:g/^<inputs>/+,/^<\/inputs>/-d|-r dummy

Which first deletes everything in the given pattern, than uses the :r command to read the data from file dummy. Now if you need to extract the filename from the lines between the <input>/</input> pattern, it gets a litte bit more complicated.

2

I feel a macro will be a easier approach. Especially, if you have various places to do the same.

  1. Keep only those 2 files open.
  2. Now in the first file start recording by qa, (a is the record name).
  3. Search for the start pattern, press down, then press Ctrl-v, search for the end pattern, press up. Now the part which you want to delete is selected. Delete it.
  4. Mark the position using ma (a is the mark name) and save the file.
  5. Go to the 2nd file, by :b2 command and similarly select the part as per your start/end criteria.
  6. Come back to this file press a to go to that point. Paste there by p.
  7. Search for end criteria again, and cross the section to not hit it again.
  8. Quit recoding by pressing q.
  9. Repeat the number of times by 40@a.
1
  • 1
    Thanks for the suggestion, it's fine, but it won't help in this particular case, since I'm looking for non-interactive solution as part of ex script where I can specify which file to insert depending on the user input arguments.
    – kenorb
    Feb 2, 2016 at 12:35
1

If this is another attempt to parse XML with non-XML Tools, don't.

The solutions here are valid, but the intention might not be. If the XML is not from a well tamed source, you might get escapes, CDATA, or white spaces in the tag!

So xsltproc from command in vi can deal with that.

1
  • The file is not actually proper XML format, but it's actually not well documented ini file having those 3 XML-like tags.
    – kenorb
    Feb 2, 2016 at 12:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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