37

I currently deal with lots of json files and am quite fond of the bash comand jq, e.g. if I want to format the file I do

jq . ugly.json > formated.json

Now, I don't want to leave vim, but run it directly within my current open file.

How can I do it?

I tried:

:!jq . %

! runs a bash command % means the current file

Yet this only displays the result, yet does not update the content of the file.


While writing this question I stumbled upon a solution yet don't know why it works. It would be great if someone could point out why :%!jq . works.

37

:!{cmd} sends {cmd} to the shell which executes it and shows its output on the screen.

:{range}!{filter} sends the lines from the current buffer inside {range} as the input of the {filter} program and replaces them with its output.

In your example, :%!jq :

  • % is the range, which means: all the lines of the current buffer
    It could also be written 1,$ (from the first line to the last one)
  • jq is the filter program

Here's a page describing the various ways of writing a range.
And on this page, you can find other usage examples of a filter program :

:%! xxd [-r]
:%! column -t
:%! sort

The first one replaces a binary file with a hex dump (or the reverse with the -r flag).
The second one formats data from the file into a table.
The third one sorts lines according to the first characters.

You can test the third one, with this simple file:

3 !
2 world
1 hello

After typing :%! sort, the buffer is replaced with:

1 hello
2 world
3 !

If you had typed :!sort %, the output would have been displayed on the screen but would not have replaced the buffer.

Note that the % sign doesn't mean the same thing depending on where it is placed:

  • Before the bang (and more generally before most Ex commands), it's interpreted as a range (same as 1,$) which tells vim which lines must be filtered.

  • After the bang (and more generally after most Ex commands), it's expanded as the name of the current file before the full command is sent to the shell.

For more information, see:

:help filter
:help range
13

By sheer luck I found out that both:

:%!jq . %

or shorter:

:%!jq .

work, yet I don't understand why this updates the buffer.

  • 1
    Because if the % is a range (i.e. is right after the :), the command (in this case jq etc) acts on the current buffer. It's similar to how something like :%s/search/replace/ works on the current buffer. – Flurrywinde Sep 17 at 18:41

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