I have a multitude of files in each of which I want to, say, delete lines 1 through 55, add a comment leader (e.g., //) on lines 25 through 35, and then save the changes to a new file.
How can I do this automatically with Vim alone or together with the help of a Bash script?
Despite the fact that using ed or sed is a common practice1
in such cases, sometimes using Vim is much more convenient. Indeed,
instead of writing an ed-like script somewhat blindly, it is often
easier to first perform the desired manipulations with one of the
files interactively in Vim:
vim -w log.vim file1.txt
and then repeat it on the rest of the files:
for f in file*.txt; do vim -s log.vim "$f"; done
For your example use case, the log.vim file will likely have
contents similar to the following:
gg55dd:25,35s/^/\/\/ /
:w %_new
:q!
Note that to save the file with new name you should not type it
directly, but rather use the % substitution, as shown above—otherwise
all the modifications of all the following files will be saved to the
same file, overwriting its contents every time. Alternatively, you can
duplicate the files beforehand and then edit the copies in place
(saving each of them by simply issuing the :w command without
arguments).
The advantage of this approach is that you can make all of the changes
interactively, ensuring that you are getting the intended result at
least on one of the files, before the edits are performed for the rest
of them.
1 Of course, you can use Vim in an ed-like fashion, too:
for f in file*.txt; do vim -c '1,55d|25,35s/^/\/\/ /|w! '"${f}_new"'|q!' "$f"; done
You can accomplish this elegantly with ed, the unix line editor, the ancestor of vi.
fix_file () {
ed -s "$1" <<-'EOF'
1,55d
25,35s_.*_// &_
wq
EOF
}
Now, for each file F you need, just execute fix_file F.
Related
I have read most questions regarding this topic, but can't get an answer to my specific question:
I have a number of files in a directory, and I want to apply a command to each of these files and then create a new file with the outpot for every single file. I can only manage to write it into one file alltogether. As i expect to have ~ 500.000 files, i also would need the script to be as efficient as possible.
for f in *.bed; do sort -k1,1 -k2,2n; done
This command sorts each file accordingly and writes the ouput in the Shell - But i cannot manage to write to file in the for-loop without appending it with ">>" .
I'm thankful for any answer providing an approach or an already answered question on this topic!
You can use script like this:
for f in *.bed
do
sort -k1,1 -k2,2n $f >>new_filename
done
If you want to be sure new_filename is empty before run the loop you can clear the content in file with command (before for loop):
>new_filename
Sometimes when working on my scripts in vim, I need to create some test data in temporary files, with multi-line content with minimum effort for typing. I tried using the usual
cat << EOF ...
in the vim command line, but it looks like vim interprets the first <cr> as the end of the command:
/bin/bash: warning: here-document at line 0 delimited by end-of-file (wanted 'EOF')
Is there some way to enter multi-line input in the command line buffer or maybe another way to write given multi-line data into files w/o exiting vim, running :sh or creating new buffers?
I the feel the simplest solution is likely typing directly into whatever file you're editing and then visually selecting it with v`[ or v{. Then you can type : to operate on the visually selected text and use :w yourfilename.txt to write it to a file. Delete the text or hit u for undo.
This isn't exactly what you asked for though so here are some other options:
Option 1
Abuse :!tee myfile. Here you'll be redirected to an instance of tee which will write stdin to stdout as well as to your specified file myfile. Things will print twice on the screen and you'll have to CTRL-C out of it, but it gets the job done just not exactly how you wanted.
Option 2
command! -nargs=1 Scratch call Scratch("<args>")
fun! Scratch(args)
exe 'e ' . a:args
call feedkeys(":append^M")
endfun
This will make a command :Scratch that takes a filename and then opens a new buffer of that file name and hacks calling the :append command which will append a multi-line string of text to the current buffer. You can end the multi-line string with . or CTRL-C. You would then have to save the buffer and return to the original one though.
Note that there are no such things as heredocs or multi-line strings in vim command mode. There are however special commands that allow them for scripting languages such as :py <<EOF (also ruby, lua, etc.).
I often find myself using something like:
sed -ri 's/<\/(abc|def ghi|j klm)>//g' someFile.html
to perform substitutions on multiple possible patterns, in this case, a closing html tag to be deleted, saving me the time and effort to do this three separate times for three closing tags I want deleted.
Is there a way to do this using substitute on vim's cli? I haven't yet found a way to do it, but it would be more efficient than going to a terminal cli or running sed from within vim if it could be done natively instead.
Yes, you don't need to use an external program at all:
:%s#</\(foo\|bar\|baz\)>##g
You can use the silent ! command to silently execute shell commands from inside vim:
:silent !sed -ri 's/<\/(abc|def ghi|j klm)>//g' %
This will execute the command in the shell silently (it won't take you away from vim to see any shell output). The % means the current buffer name. Vim will then notify you that the file you are editing has been changed and will ask you if you want to load the changes, press l for load and the new changes from the sed shell command will appear.
What would be the most practical way to rename the file you're currently editing in Vim without messing up your current splits configuration?
Generally, one would need to ... save the file under a different name, delete the original one, and re-open the new one without making a mess of the current layout.
Anyone have any idea how to do that in one command (function) or less?
:saveas newname will save the buffer with the new name, make that name the current buffer, and set the alternate buffer to the old file.
:call delete(expand('#')) will then delete the file associated with the alternate buffer.
You can easily turn that into a command with something like
:command! -bang -complete=file -nargs=+ Rename saveas<bang> <args> | call delete(expand('#'))`
The user manual provides a thorough description of how to create user commands. Here's an explanation of the elements I'm using above.
-bang allows the command to called as either Rename or Rename! and <bang> in the constructed command is replaced by either an empty string or !, depending on how it is called. This is used to support the same functionality in the :saveas command.
-complete=file will let you tab-complete the path that will be used for the new file, similar to :e and :saveas do.
-nargs=+ specifies that :Rename requires at least one argument (the filename), but can take more. <args> is replaced with whatever arguments are given to :Rename. This allows you to specify the extra arguments that :saveas accepts, so you could do something like :Rename ++enc=latin1 newfile to rename the file to newfile and change the encoding to latin1.
Tim Pope has a plugin that has a function :Rename that does this: vim-eunuch.
You can also do the following sequence of steps:
:saveas newfile
:bw <buffer_for_the_old_file>
:!rm old_file
of course this is not as nice as renaming the file in the shell.
Call up the explorer with :Explorer or just :E, select your file, and then press r to rename.
Use :Move provided by eunuch.
eunuch also provides other useful file operations, like :Remove, sudoedit.
I know that you can use m and then a to add a child node in NERDTree to add files or directories one at a time. Is there anyway to type out a list, e.g. {Book,Author}.php and get a corresponding file or directory for each one in the list?
If on Bash Shell, I would do this in Vim command mode:
q:i!touch {Book,Author}.php
q:i -- Enter into Vim command history window (q:), then insert (i), it also let you edit commands with all Vim's editing power.
! -- To use shell
**touch mydir/{my_file_1,my_file_2}.php -- This will create the empty files.
actually, do it in Bash Shell will be simpler.