I would like to write a python script that parses some vim data and returns a result. For example, we'll turn numbers, separated by lines, into a sorted list of unique numbers. If we had this as the input:

enter image description here

The python script would do:

import sys
sys.stdout.write('\n'.join([str(i) for i in (sorted(set([int(k) for k in sys.argv[-1].split()])))]))

This would expect the text from vim to be passed as an arg, so perhaps something like:

:imap <c-t> <c-r>=system('x.py <text_from_vim>')<cr>

And now vim would look like:

enter image description here

How could this be done?

  • 1
    Filter? :%! ...
    – B Layer
    May 31 '20 at 2:44
  • 1
    What it does is take a range of lines and sends them as stdin to a command and the result replaces those lines. So, for example, :%!sort will sort the entire file (if you have a sort command in your shell) since % is short hand for "the whole file".
    – B Layer
    May 31 '20 at 2:50
  • 1
    That's right.. yep. (You don't need to enter the range if you hit : from Visual mode. It's prepopulated.)
    – B Layer
    May 31 '20 at 2:56
  • 2
    Great opportunity to learn how to call external filters, but :%sort | %!uniq should do it, or :%!sort | uniq if you want to use external sort (I dont think vim has a builtin :uniq but I could be wrong).
    – D. Ben Knoble
    May 31 '20 at 14:16
  • 2
    @D.BenKnoble You can pass u to :sort to remove duplicates. For this example: :4,11sort nu
    – Rich
    Jun 17 '20 at 13:29

Thanks to @BLayer for the suggestion here. Here is a 5s video of the result.

In summary, what was done is:

  • The external (or internal) script will receive the lines as input to stdin. If sending the full file, you can use :%!, and if sending a visual selection, selecting the text and entering cmd mode will automatically do this, '<,'>.
  • In the python (or any other extern script) you'd read stdin as text. In python, this is currently done by using sys.stdin.read(). So the full interaction looks like:

In vim:


In python:

import sys
input_string = sys.stdin.read()
sys.stdout.write('\n'.join([str(i) for i in sorted(set([int(k) for k in input_string.split()]))]))

Additionally, you can pass command-line args by just entering them right into vim, for example:

:'<,'>!./x.py arg1 arg2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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