How to replace the contents of the current buffer with the contents of a file? - vim

I have an external script that takes a Javascript file and automatically fixes some style issues, I want to apply it to the current buffer right before writing (BufWritePre,FileWritePre).
So my idea is to:
w /tmp/foo Write the current buffer contents to a temporary file
silent !fixStyle /tmp/foo Run the script on that file.
Replace the contents of the current buffer with the contents of /tmp/foo
The thing is that I don't know how to do the third step.

One way is deleting current contents (1,$d, i.e., delete between line 1 and last line), and read in the target file starting from line 0 (before line 1 so that there is no blank line):
:1,$d|0r ~/.hck/input
Another way is using a filter (cat in this case) to replace all of the content (%) with the output of the filter:
:%!cat /tmp/foo

You could use set autoread. It detects when the file has been changed on disk and reloads the current buffer from the file. So you could save the file, run the script and vim will reload the buffer with the change contents.
Autoread's help is copied below
*'autoread'* *'ar'* *'noautoread'* *'noar'*
'autoread' 'ar' boolean (default off)
global or local to buffer |global-local|
{not in Vi}
When a file has been detected to have been changed outside of Vim and
it has not been changed inside of Vim, automatically read it again.
When the file has been deleted this is not done. |timestamp|
If this option has a local value, use this command to switch back to
using the global value: >
:set autoread<

Related

how to remove new lines in vi editor linux?

I am using Linux (centos flavor) and created a file with the following text:
" hello
world
"
Question:
Now, I opened the file in vi editor mode and am able to remove all non blank characters(backspace or delete keys do nothing).
But newline characters persist and I get error saying "no previous regular expression".
What should I do to remove all the new lines so that my file is just empty?? I have tried backspace key many times but no effect and I do not want to use cat > filename to just overwrite the file to make it empty!
You can use dd to delete any lines in vi editor.
Example:
You have a file having 6 lines and you want to delete all 6 lines:
Open the file using 'vi` editor
Go to first line
use 6dd
:g (for global) could help you here.
:g/^$/d basically says that "globally find any pattern matching ^$ and delete those".
If you think that you might have blanks in those lines, you could say ^\ *$
open txt with vi
:1 << move cursor to first of file
d << enter delete mode
G << move cursor to end of file
It will remove all from cursor( in this case, at first of file ) to end of file
or
open txt with vi
d
N (Number, As many as you want to delete lines)
Enter

How to use sed command to delete lines without backup file?

I have large file with size of 130GB.
# ls -lrth
-rw-------. 1 root root 129G Apr 20 04:25 syslog.log
So I need to reduce file size by deleting line which starts with "Nov 2" , So I have given the following command,
sed -i '/Nov 2/d' syslog.log
So I can't edit file using VIM editor also.
When I trigger SED command , its creating backup file also. But I don't have much space in root. Please try to give alternate solution to delete particular line from this file without increasing space in server.
It does not create a real backup file. sed is a stream editor. When applied to a file with option -i it will stream that file through the sed process, write the output to a new file (a temporary one), when everything is done, it will rename the new file to the original name.
(There are options to create backup files also, but you didn't give them, so I won't mention that further.)
In your case you have a very large file and don't want to create any copy, however temporary. For this you need to open the file for reading and writing at the same time, then your sed process can overwrite the original. After this, you will have to truncate the file at the end of the writing.
To demonstrate how this can be done, we first perform a test case.
Create a test file, containing lots of lines:
seq 0 999999 > x
Now, lets say we want to remove all lines containing the digit 4:
grep -v 4 1<>x <x
This will open the file for reading and writing as STDOUT (1), and for reading as STDIN. The grep command will read all lines and will output only the lines not containing a 4 (option -v).
This will effectively overwrite the beginning of the original file.
You will not know how long the output is, so after the output the original contents of the file will appear:
…
999991
999992
999993
999995
999996
999997
999998
999999
537824
537825
537826
537827
537828
537829
…
You can use the Unix tool truncate to shorten your file manually afterwards. In a real scenario you will have trouble finding the right spot for this, so it makes sense to count the number of bytes written (using wc):
(Don't forget to recreate the original x for this test.)
(grep -v 4 <x | tee /dev/stderr 1<>x) |& wc -c
This will preform the step above and additionally print out the number of bytes written to the terminal, in this example case the output will be 3653658. Now use truncate:
truncate -s 3653658 x
Now you have the result you want.
If you want to do this in a script, i. e. without interaction, you can use this:
length=$((grep -v 4 <x | tee /dev/stderr 1<>x) |& wc -c)
truncate -s "$length" x
I cannot guarantee that this will work for files >2GB or >4GB on your machine; depending on your operating system (32bit?) and the versions of the installed tools you might run into largefile issues. I'd perform tests with large files first (>4GB as this is typically a limit for many things) and then cross your fingers and give it a try :)
Some caveats you have to keep in mind:
Of course, nobody is supposed to append log entries to that log file while the procedure is running.
Also, any abort during the running of the process (power failure, signal caught, etc.) will leave the file in an undefined state. But re-running the command again after such a mishap will in most cases produce the correct output; some lines might be doubled, but not more than a single line should be corrupted then.
The output must be smaller than the input, of course, otherwise the writing will overtake the reading, corrupting the whole result so that lines which should be there will be missing (or truncated at the start).

In Vim, run command and redirect stdout to specific buffer

Say I open two buffers side by side and enter the source code into buffer 1. I want to run the compiler (or any command line program) and see its output (stdout) in buffer 2.
How do I feed the current or specific buffer as stdin to this command line program? If this it not possible, I can save source code to the file and specfy it as parameter to compiler; but anyway I want to see output in buffer 2.
If you look at :h :b:
:[N]b[uffer][!] [+cmd] [N] :b :bu :buf :buffer E86
Edit buffer [N] from the buffer list. If [N] is not given,
the current buffer remains being edited. See :buffer-! for
[!]. This will also edit a buffer that is not in the buffer
list, without setting the 'buflisted' flag.
Also see +cmd.
And for +cmd:
+cmd [+cmd]
The [+cmd] argument can be used to position the cursor in the newly opened
file, or execute any other command:
+ Start at the last line.
+{num} Start at line {num}.
+/{pat} Start at first line containing {pat}.
+{command} Execute {command} after opening the new file.
{command} is any Ex command.
So:
:2b +r!date
Would open buffer 2, and read in the output of the date command.
You can use :r! command to execute shell command and read its output to current buffer.

Vim - How to insert a backslash at the start of a line in a new file using autocmd and a template file

I followed this guide to automatically insert different header templates into new files of different types based on the file extension:
http://www.thegeekstuff.com/2008/12/vi-and-vim-autocommand-3-steps-to-add-custom-header-to-your-file/
It works great! I have a custom header for python source files that gets inserted automatically when I open a new .py file.
I want to do a similar thing so that a basic LaTeX template is inserted when I open a new .tex file...
Except I can't get it to work...
My ~/.vimrc says this:
autocmd bufnewfile *.tex so /home/steve/Work/tex_template.txt
and my tex_template.txt says this:
:insert
\documentclass[a4paper,12pt]{article}
.
but when I open a new file like this:
vim test.tex
(where test.tex does not exist already)
I get this:
"test.tex" [New File]
Error detected while processing /home/steve/Work/tex_template.txt:
line 2:
E492: Not an editor command: :insertdocumentclass[a4paper,12pt]{article}
Press ENTER or type command to continue
The problem appears to be with the backslash at the start of the line because if I delete the backslash from tex_template.txt the the new file opens up with documentclass[a4paper,12pt]{article} in it. Except I need the backslash because otherwise it's not a tex command sequence.
If you look at :help :insert it says this:
Watch out for lines starting with a backslash, see
line-continuation.
Following the link to line-continuation explains that the \ is a continuation character which can be overridden by passing the C flag to cpoptions.
It should work if you change your template as follows:
:set cpo+=C
:insert
\documentclass[a4paper,12pt]{article}
.
:set cpo-=C
You might want to consider using a snippets engine like vim-snipmate or (my favorite) ultisnips. With those you can insert snippets of text everywhere, not just at the beginning of a file.
As a bonus, these snippets can e.g. substitute variables and even run commands. The following is my snippet (for ultisnips) set to produce the header for a TeX file;
snippet hdr "File header for LaTeX" b
% file: `!v expand('%:t')`
% vim:fileencoding=utf-8:ft=tex
%
% Copyright © `!v strftime("%Y")` ${1:R.F. Smith} ${2:<my#email>}. All rights reserved.
% Created: `!p snip.rv = fcdate(path)`
% Last modified: `!v strftime("%F %T %z")`
$0
endsnippet
This will automatically fill in the file name and the time when the file was last modified. It fills in my name and e-mail with default values but gives me the opportunity to override them. The fcdate function is a piece of Python code that I wrote to retrieve the birthtime of a file.
I have the hdr snippet defined for several different filetypes, and a general one that is used for all other files. If I type hdrtab at the beginning of a line, the appropriate snippet is expanded.

vim - process the output from the "read" command as a range in Ex mode

BACKGROUND:
In vim (Ex mode) it is possible to run an external command and have the output from that command automatically inserted into the current buffer.
In Example 001, we see how to cd to the current directory, get a listing of the files there and auto insert that into the current buffer
EXAMPLE 001:
:cd . | :r ! dir /w/s/b
QUESTIONS:
1) Is it possible to automatically specify or capture the Vim {range} to reflect the lines that were recently inserted into the file ?
2) Using the range obtained in question 1) is it possible to chain Ex mode commands to automatically process the lines that were inserted into the file ?
3) If it is not possible to do 1) or 2) above, is there an alternate way for Vim to recognize lines recently inserted into the buffer and run arbitrary commands on them ?
4) What is a relevant :help cross reference that can be used for this purpose ?
GOAL:
The goal is to be able to chain multiple Ex mode commands together to easily run process recently added lines to a file, without having to expressly identify the line number or manually select them using Visual mode or something similar.
The goal is do something similar to the (psuedo-code) in Example 002
Example 002:
:cd . | :r ! dir /w/s/b | :{auto-range}s/^/ /
Vim sets the change marks '[ and '] to the inserted range; you can use these to define a range for subsequent Ex commands:
:cd . | execute 'r ! dir /w/s/b' | '[,']s/^/ /
You need :execute because otherwise the | is interpreted to belong to the :r command.
What about processing those lines before inserting them in Vim?
:r!dir /w/s/b | sed -e "s/^/ /"

Resources