5

Suppose I want to edit a new libvirt XML file:

virsh dumpxml machine1 > machine2.xml

Then I open the file with vim,

vim machine2.xml

and then I want to change the UUID. Now do this, I use

:r ! uuidgen

But the new UUID is inserted under the current line:

<uuid>6r123ttz-6t01-356t-1y7h-g7oe196c8u3m</uuid>
e67baf90-751b-4aea-a14b-aca1b881542b

How may I insert the output of the uuidgen command into the current line, so that if I have

<uuid></uuid>

(with the cursor between ><), then, by executing uuidgen I get

<uuid>e67baf90-751b-4aea-a14b-aca1b881542b</uuid>

The question is: is possible to use the :r vim command without adding a new line?

2

5 Answers 5

7

:read is an Ex command; these unfortunately always work on complete lines. You either have to work around that by first splitting and then later joining with the preceding and following lines, or you use a different command.

The :help i_CTRL-R command can insert any register contents. There's a special expression register (:help quote=) that executes any Vimscript expression and uses its result. Finally, the system() function executes an external command and returns its output.

So (in insert mode) <C-R>=system('uuidgen')<CR> will insert the command's output at the current position. Unfortunately, the output has a trailing newline, and that is preserved, so </uuid> moves to the next line. To avoid that, you can truncate the output, either via system('uuidgen')[0:-2] or substitute(system('uuidgen'), '\n$', '', '') as the expression.

For repeated inserts, the expression history can be used to recall the last expression (by pressing <Up>); for even more comfort, you can define a mapping for the whole thing.

2
  • This works great! I was wondering how I would map the following sequence to a normal mode mapping: 1. visual highlight of a current UUID. 2. 'c' to enter insert mode, and finally 3. executing the <C-R>=system .... I'm already stuck at figuring out how to to step 1. the dashes in UUIDs make the selection of one whole UUID a bit challenging (other that using 'inside double-quote' for cases where the UUID are enclosed in quotes) Commented Apr 29, 2021 at 9:43
  • 1
    @ChristophWegener As long as you don't have to deal with the two dashes / non-dashes variants, the length of a UUID should be constant, right? So v25l (selecting 36 characters for the dashed variant) would do the selection, assuming you're at the start. However, I would favor a plugin-based solution (e.g. using my TextTransform plugin), and those typically allow selection of the target text via a regular expression ('\%(^\|\X\)\zs\x\{8}-\?\x\{4}-\?\x\{4}-\?\x\{4}-\?\x\{12}\ze\%(\X\|$\)'. Commented Apr 30, 2021 at 8:36
4

I had a play around and this doesn't do exactly as you want as :r will always enter a newline. But it should do what you want for a result.

:.!UUID=`uuidgen` ; echo "<uuid>$UUID</uuid>"

It puts the stdout on the current line (not at the cursor) - the smarts are done with the command that is being invoked.

2
  • It create also a newline, is a nice workaround, so for the moment I consider as right answer
    – elbarna
    Commented Mar 25, 2019 at 22:47
  • I know why - use printf instead of echo and it should be fine
    – hqqns
    Commented Feb 18, 2023 at 1:09
4
fu! SetBang(v) range
        if a:v == 1
                normal gv
        endif
        let l:t = &shellredir
        let &shellredir = ">%s\ 2>/dev/tty"
        let @" = join(systemlist(input("\"!"))," ")
        let &shellredir = l:t
endf
nnoremap "! :cal SetBang(0)<cr>
xnoremap "! :cal SetBang(1)<cr>

Every time you press "!, the user will be prompted for a command, and the "unnamed" " register will be filled with it. You can then insert it as usual, with P in normal or visual mode, with Ctrl-R" in insert mode, etc.

For instance, having the text (caret showed by |):

<uuid>|</uuid>

and pressing "!uuidgen<Enter>P will insert the new uuid at the caret.

You could of course use other binding instead of "!.

1

No but you do the following sequence.

Place cursor where you want to insert

<uuid>|</uuid>

Press i <CR> ^[ k :r !uuidgen k 3J x

1

You can also edit the XML directly without even using vim.

For example given <uuid></uuid> as part of the file you can insert or update the value:

echo '<uuid></uuid>' |
    xmlstarlet edit --update '//uuid' -v "$(uuidgen)"

Output

<?xml version="1.0"?>
<uuid>79cc09da-a256-46d5-9974-9073cf9d7a9a</uuid>

This can allow you to template VM definitions and customise them programmatically.

You must log in to answer this question.

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