36

I would like to know if there is a way to make Vim behave like tail -f.
Even the best Vim plugin I've found so far doesn't do what I expect.

I really want to see the file update in real-time. Even I'm away from keyboard, I want Vim to constantly reload the buffer and jump to the last line.

How to do this?
(I don't want to reload the whole file, since some log files are very big. The best is to only load the last lines, like tail -f does.)

migrated from serverfault.com Jul 7 '13 at 0:58

This question came from our site for system and network administrators.

  • 2
    Why must it be within vim? What's wrong with tail -f? – Sven Jul 6 '13 at 14:16
  • 1
    Because I want the power of vim when I read logfiles. Like being able to search for pattern, and especially enjoy the syntax highlighting. I made my own syntax highlighting for some log files (bind, apache, etc.). – Fox Jul 6 '13 at 14:21
  • You could be interested in autoread and FileChangedShell >:help autoread >:help FileChangedShell – gsi-frank Jul 6 '13 at 14:29
  • 2
    For just the searching, you could do less +F instead of tail -f. Doesn't give you the syntax highlighting though. – Dennis Kaarsemaker Jul 6 '13 at 18:00
  • 1
    It can: superuser.com/questions/71588/how-to-syntax-highlight-via-less – user26112 Jul 7 '13 at 1:10
28

You can't make vim behave like tail -f. You can make less behave like a combination of vim and tail -f though.

Forward forever (follow) mode

less has a forward forever mode that you can enter by pressing F or by passing +F to it as an argument.

$ less +F

In this mode, less behaves like tail -f in that it doesn't stop reading when it reaches the end of a file. It constantly refreshes with new data from the file. To exit this mode, press Ctrlc.

Syntax highlighting

less supports automatic filtering of the data it reads. There is a program called source-highlight that can perform basic source code highlighting. It comes with a script that works well with less. To use it, just set the LESSOPEN environmental variable appropriately.

 export LESSOPEN="| /path/to/src-hilite-lesspipe.sh %s"

You also have to tell less to pass raw terminal escape sequences (these tell your terminal how to color text) by passing it the -R flag. You can tell less to pretend it is always being passed the -R flag by setting the LESS environmental variable.

 export LESS=' -R '

When less isn't enough

Although less has vi-like keybindings, it just isn't the same as Vim. Sometimes it feels foreign and it lacks important features such as ctags integration and the ability to edit text.

You can make less call Vim (assuming EDITOR=vim) on the file it is currently viewing by pressing v. less will even put your cursor in the correct location within Vim. When you exit Vim, you will find yourself back at less. If you made any changes to the file while you were in Vim, they will be reflected in less.

5
vim --servername TAIL_VIM /tmp/somefile

Now, in another shell (like bash), you can do:

while true
do
    inotifywait --event modify /tmp/somefile \
    && vim --servername TAIL_VIM --remote-send '<C-\><C-N>:edit!<CR>G';
done

Where the < C->< C-N> forces vim to go to normal mode, "edit!" tells vim to reload the current file (the CR simulates pressing enter), and G goes to the bottom of the file. Removing the (G) makes it easier to insepct the file while input is coming in.

  • I see the param "--servername" in the man, however I got following error message: VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Feb 10 2013 02:28:47) Option inconnue: "--servername=toto" Plus d'info avec: "vim -h" – Fox Aug 5 '14 at 10:52
  • if you do vim --version do you see +clientserver (not -clientserver, with a hypen)? If not, many people compile vim from source using ./configure --with-features=huge – crutux Aug 5 '14 at 17:35
  • Yes, there is +clientserver. I have the Ubuntu version. – Fox Aug 5 '14 at 20:34
1

I found the answer thanks to Evan Teitelman's comment. My solution is inspired from /usr/share/vim/vimcurrent/macros/less.vim.

I made my own function, but it could be improved a lot.

function! Tailf()
    e
    normal G
    redraw

    sleep 1
    call Tailf()
endfunction

And just hit CTRL+C to exit Tailf.

To open a log file in Tailf mode: view -M "+call Tailf()" /path/to/logfile.log

What I don't like, is the sleep call, which doesn't allow to do anything in vim during the auto-refresh. The best would be if the buffer is autonomous and updates even if I'm in another split-window. Anyway, it's a good start. :)

  • 2
    It looks like it also rereads the entire file, which is not what you wanted, right? – Bernhard Jul 7 '13 at 6:31
  • This does reload the entire file. It probably wouldn't be too difficult to make a plugin based on inotifywait and :read that waits until a file has been modified and if it has been appended to, reads in the lines at the bottom of the file. – user26112 Jul 7 '13 at 13:46
  • Yes, it's true, it reloads the whole file, so this has to be a lot improved. When I say that I want this, I'm more talking about the result (what you see at the screen : a vim which updates your file alone and stays on the last line). Unfortunately I don't have enough vim knowledge to make a nice plugin... – Fox Jul 7 '13 at 15:51
  • @EvanTeitelman OK, I see how to use inotifywait now. I can know whenever the file changes. But how to know if it has been appended (not completely modified), and how to know how many new lines have been appended? – Fox Jul 7 '13 at 16:44
  • Read the number of lines in the file each time the file changes. If the number of lines increases, read in the changes. – user26112 Jul 7 '13 at 17:25
1

A non recursive method for the same:

cmap tailf<CR> call Tailf()<CR>
         function! Tailf()
    "Press C-c to stop tailing
        while 1
            e                                  
            normal G
            redraw
            sleep 1
        endwhile
    endfunction
1

I like it short and without a lot of hacking. You can run this oneliner from ex (whithin vim) when needed (or put each command in vimrc, for when log-files are opened.)

:set autoread | au CursorHold * checktime | call feedkeys("lh")

(if you would want to jump (nearly) to the end of the file, just use "G" instead of "lh" with feedkeys)

Explanation:

  • autoread: reads the file when changed from the outside (but it doesnt work on its own, there is no internal timer or something like that. It will only read the file when vim does an action, like a command in ex :!
  • CursorHold * checktime: when the cursor isn't moved by the user for the time specified in updatetime (which is 4000 miliseconds by default) checktime is executed, which checks for changes from outside the file
  • call feedkeys("lh"): the cursor is moved once, right and back left. and then nothing happens (... which means, that CursorHold is triggered, which means we have a loop)

To stop the scrolling when using call feedkeys("G"), execute :set noautoread - now vim will tell, that the file was change ans ask if one wants to read the changes or not)

I like the idea to watch logfiles in vim (instead of tail -f), e.g. when you are working in an ssh session without screen/tmux. Additionally you can copy directly from the logfile, if needed, or save the output directly or ... whatever you can do with vim :)

*from this answer (refering to an answer by PhanHaiQuang and a comment by flukus)

0

Edit: I apologize, completely bypassed that you already tried this.

You can try using the Tail Bundle plugin, I think it should do what you are looking for.

To install, open tail-3.0.vba with vim and execute:

:so %

Restart and you should be able to tail a file from within vim like so:

:Tail /path/to/file
0

ONLY For VIM 8.0+ they introduced timers, so you could use a timer to simulate tail function, this works pretty effectively in my experience.

I have mapped F6 key to turn on tail and F7 to turn off tail, you can customize those in the commands below, also see my section below for WORDS OF CAUTION.

Run the following

exe ":function! Tail(timer) \n :exe 'normal :e!\<ENTER>G' \n endfunction"
map <F6> :exe timer_start(2000, 'Tail', {'repeat':-1})<CR> 
map <F7> :exe timer_stopall()<CR>

You can also make the above commands part of vimrc so you always have them.

Some words of caution:

  • I assume you don't have any other timers, so timer_stopall() will actually effectively stop all the timers. You could modify this a bit further to get the ID during timer_start and then hold it, etc but that seemed a bit of work to me and I'd assume most people wouldn't be using timers.
  • while it is in tail mode, I would highly suggest you don't try to run any commands, it usually crashes the vim instances, so PLEASE TURN OFF TAIL (F7) before you decide to go back into editing mode, this is STRICTLY ONLY FOR VIEWING LOGS.
-1

From within Vim, execute:

!tail -f 

That will suspend Vim while the forked off child executes tail -f on the current buffer. Hitting Ctrl-C returns you back to the buffer in Vim.

  • 1
    That's not vim, but plain tail doing the work. Vim is portable. This is not. – oligofren May 22 at 12:32

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.