Vim Tips Wiki
(reword and redo script)
(add filter example)
Line 55: Line 55:
   
 
==Using a filter command==
 
==Using a filter command==
  +
A filter is a program which reads text from standard input, then processes the text, and sends the results to standard output. In Vim, a range of lines can be selected, then replaced with the output from running a filter (the selected lines are the input to the filter).
{{help|:range!}} gives details about using a filter command. This command is used to completely replace one or more lines with the output of an external command using the text currently in the lines as input. See {{help|filter}}.
 
   
  +
For example, the following text may appear in a file that is being edited:
''need useful example here''
 
  +
<pre>
  +
One,Two,Three,Four,Five
  +
arborist,apple,artichoke,ant,author
  +
branch,banana,broccoli,bee,book
  +
canopy,cherry,cabbage,cricket,codex
  +
</pre>
  +
  +
The following procedure uses the <tt>cut</tt> utility (available on many Unix-based systems) to replace each line with fields 2 to 3 inclusive:
  +
*On the first line, press V to start a visual selection.
  +
*Press j to move the cursor down until all wanted lines are selected.
  +
*Press ! (the command line will show :'<,'>! indicating that the selected range will be filtered).
  +
*Enter a command to be executed by the shell, such as {{tt|cut -f2-3 -d,}} (select fields 2-3 using comma as a delimiter between fields).
  +
  +
Vim saves the selected lines to a temporary file, then runs the external command with the temporary file as input. The result from running the command replaces the selected lines. In this example, the result is:
  +
<pre>
  +
Two,Three
  +
apple,artichoke
  +
banana,broccoli
  +
cherry,cabbage
  +
</pre>
  +
  +
See [[Use filter commands to process text#Simple filter example|this example]] using Python, and {{help|filter}}.
   
 
==Using backticks==
 
==Using backticks==
Line 89: Line 111:
   
 
==Comments==
 
==Comments==
 
 
If you want capture output to new unnamed buffer and
 
If you want capture output to new unnamed buffer and
 
<pre>
 
<pre>

Revision as of 09:11, 11 April 2011

Proposed tip Please edit this page to improve it, or add your comments below (do not use the discussion page).

Please use new tips to discuss whether this page should be a permanent tip, or whether it should be merged to an existing tip.
created February 9, 2010 · complexity basic · author Perlsomian · version 7.0

This tip shows how to capture the output from running an external (or shell) command in Vim. Vim has many useful functions which can replace shell commands, such as strftime() or glob(), but sometimes only the shell command will do. See :help function-list for a list of Vim's built-in functions.

The results from running a shell command can be inserted into the current buffer with the :read command, or lines can be replaced in a buffer with a filter command, or Vim's system() function can be used like the backquote syntax (aka backtick) that many Unix shells provide to capture command output, which you can then use in a script or expression-register (:help quote=) to insert in the buffer or parse in some way.

The following examples capture the output of the shell's date command. This is just an example: using Vim's strftime() function would be a better choice if you need the date or time in a script.

Using :read

The :read command can insert a file or the result from running an external program into the current buffer. To run a program, preface the shell command with ! (see :help :read!). For example,

:read !date

inserts the current date on a new line below the current line on most Unix-like systems (on Windows, use !date /t instead).

If a line number is specified, the new text is inserted after that line. For example, :12read !date inserts the result after line 12, and :$read !date inserts the result after the last line. To insert the result before the first line, specify line 0:

:0read !date

Using system()

If you don't want to the command output on a line by itself, or if you don't want it inserted, you can use the system() function. For example, to put the current date into a variable named curdate, which you can then use inside a script, use:

:let curdate=system('date')

Using system() is the most flexible method as it allows a script to process the result before any output. For example, the function below appends the output of the command (if successfully executed) to the end of the current line. As mentioned before, using strftime() is a better option for capturing timestamps, but the script demonstrates three important concepts:

  1. Use system() to capture the output of an external command in a script.
  2. Use shellescape() to escape any arguments to an external command to avoid passing possibly dangerous commands to the shell.
  3. Use setline() to change text without moving the cursor.

After sourcing the following script, press F8 to append the result from running the command to the current line. The date -u command, which works on Unix-based systems, outputs UTC time (while strftime() gives the local time).

nnoremap <F8> :call GetDate('')<CR>
function! GetDate(format)
  let format = empty(a:format) ? '+%A %Y-%m-%d %H:%M UTC' : a:format
  let cmd = '/bin/date -u ' . shellescape(format)
  let result = substitute(system(cmd), '[\]\|[[:cntrl:]]', '', 'g')
  " Append space + result to current line without moving cursor.
  call setline(line('.'), getline('.') . ' ' . result)
endfunction

Using a filter command

A filter is a program which reads text from standard input, then processes the text, and sends the results to standard output. In Vim, a range of lines can be selected, then replaced with the output from running a filter (the selected lines are the input to the filter).

For example, the following text may appear in a file that is being edited:

One,Two,Three,Four,Five
arborist,apple,artichoke,ant,author
branch,banana,broccoli,bee,book
canopy,cherry,cabbage,cricket,codex

The following procedure uses the cut utility (available on many Unix-based systems) to replace each line with fields 2 to 3 inclusive:

  • On the first line, press V to start a visual selection.
  • Press j to move the cursor down until all wanted lines are selected.
  • Press ! (the command line will show :'<,'>! indicating that the selected range will be filtered).
  • Enter a command to be executed by the shell, such as cut -f2-3 -d, (select fields 2-3 using comma as a delimiter between fields).

Vim saves the selected lines to a temporary file, then runs the external command with the temporary file as input. The result from running the command replaces the selected lines. In this example, the result is:

Two,Three
apple,artichoke
banana,broccoli
cherry,cabbage

See this example using Python, and :help filter.

Using backticks

Above it is mentioned that using system() is like using backtick expansion in many shells. It should be noted that Vim actually does support real backticks in some situations. See :help backtick-expansion for details. This means you can do things like:

:new `date`

to open a new buffer with a name matching the current date. This even works on Windows! The :help does not make it clear, but this works using the cmd.exe shell:

:new `date /t`

This also provides a way to use Vim expressions where expressions are not normally allowed. For example, rather than using:

:exe 'e' filename_in_var

you can use:

:e `=filename_in_var`

See also

Comments

If you want capture output to new unnamed buffer and

command! -nargs=* -complete=shellcmd R exe "new | setlocal buftype=nofile bufhidden=hide noswapfile | r !<args>"

" Now you can
:R find | xargs grep vim
" or
:R ls -la
" or whatever
:R mysql -vvt -e'show databases'
Doesn't the :command need to use the -bar flag here so it will see a | as one of its arguments? Without that, I don't think the
:R find | xargs grep vim
example will work. That should also remove the need for the use of :exe. JamesVega 15:52, February 17, 2010 (UTC)

Is this right?

:help shellescape() says the second argument is used to escape %, !, etc. and implies that this is mostly for use with the :! command, because otherwise Vim will interpret these characters as something else. It says these escapes are removed by the :! command before being passed to the shell.

But the above script does not use :!, it uses system(). I do not have a Unix-like system available at the moment, but in Windows :echo system('echo '.shellescape("%PATH%")) works but :echo system('echo '.shellescape("%PATH%"), 1) does not. I don't think passing in a second argument to shellescape() is correct here. Perhaps it works on Unix for some reason? Or perhaps "date" on Unix expects its arguments to be escaped? Probably a simpler example is in order after all. Won't need to figure out the regex then, either. --Fritzophrenic 03:49, April 7, 2011 (UTC)

I don't feel like grappling with the system() issue now (it does look dubious), but the example needs work: TemporalFormat only makes sense for a date function, yet as we have discussed, using an external date program is not required and is a bad example for a tip. Further, the "utilpath" is rather unusual: it may be a great idea for a particular usage, but in general people trust PATH to locate their utilities. There is a misplaced paren in your second ("does not") example; should be: :echo system('echo '.shellescape("%PATH%", 1)). JohnBeckett 10:30, April 7, 2011 (UTC)
I have checked this. The '1' is definitely wrong, although the script works on Linux both with and without the 1. I have done some heavy rewording, and I simplified the script to make it look a little more general. I have cheered up since noticing that -u displays UTC time, and that is not possible with strftime(). Please delete this after reading (if you are happy with it). I have not finished (I have some notes that I took months ago about the above comments, and hope to read them later). JohnBeckett 08:55, April 8, 2011 (UTC)