How to run a series of vim commands from command prompt - shell

I have four text files A.txt, B.txt, C.txt and D.txt
I have to perform a series of vim editing in all these files.
Currently how I am doing is open each files and do the same vim commands one by one.
Is it possible to make a script file which I can run from the command prompt, means without open the actual file for vim editing.
for example, if I have to perform the below vim commands after opening the A.txt file in vim editor:
:g/^\s*$/d
:%s/^/[/
:%s/\(.*\)\(\s\+\)\(.*\)/\3\2\1
:%s/$/]/
:%s/design/test/
Is it possible to make a script file and put all these commands including gvim A.txt (first command in the file).
and edit run the script file from command prompt.
If it is possible, please let me know how to do it and how it can be done with single or multiple files at a time?

vim -c <command> Execute <command> after loading the first file
Does what you describe, but you'll have to do it one file at a time.
So, in a windows shell...
for %a in (A,B,C,D) do vim -c ":g/^\s*$/d" -c "<another command>" %a.txt
POSIX shells are similar, but I don't have a machine in front of me at the moment.
I imagine you could load all the files at once and do it, but it would require repeating the commands on the vim command line for each file, similar to
vim -c "<command>" -c "<command>" -c ":n" (repeat the previous -c commands for each file.) <filenames go here>
EDIT: June 08 2014:
Just an FYI, I discovered this a few minutes ago.
vim has the command bufdo to do things to each buffer (file) loaded in the editor. Look at the docs for the bufdo command. In vim, :help bufdo

The amount of -c commands directly passed to Vim on the command-line is limited to 10, and this is not very readable. Alternatively, you can put the commands into a separate script and pass that to Vim. Here's how:
Silent Batch Mode
For very simple text processing (i.e. using Vim like an enhanced 'sed' or 'awk', maybe just benefitting from the enhanced regular expressions in a :substitute command), use Ex-mode.
REM Windows
call vim -N -u NONE -n -es -S "commands.ex" "filespec"
Note: silent batch mode (:help -s-ex) messes up the Windows console, so you may have to do a cls to clean up after the Vim run.
# Unix
vim -T dumb --noplugin -n -es -S "commands.ex" "filespec"
Attention: Vim will hang waiting for input if the "commands.ex" file doesn't exist; better check beforehand for its existence! Alternatively, Vim can read the commands from stdin. You can also fill a new buffer with text read from stdin, and read commands from stderr if you use the - argument.
Full Automation
For more advanced processing involving multiple windows, and real automation of Vim (where you might interact with the user or leave Vim running to let the user take over), use:
vim -N -u NONE -n -c "set nomore" -S "commands.vim" "filespec"
Here's a summary of the used arguments:
-T dumb Avoids errors in case the terminal detection goes wrong.
-N -u NONE Do not load vimrc and plugins, alternatively:
--noplugin Do not load plugins.
-n No swapfile.
-es Ex mode + silent batch mode -s-ex
Attention: Must be given in that order!
-S ... Source script.
-c 'set nomore' Suppress the more-prompt when the screen is filled
with messages or output to avoid blocking.

With all the commands you want to run on each file saved in a script, say "script.vim", you can execute that script on one file like this (as others have mentioned):
vim -c "source script.vim" A.txt
Taking this one step further, you can save your file at the end of the script, either by putting a :w command inside the script itself, or passing it from the command-line:
vim -c "source script.vim | w" A.txt
Now, you can run any command in Vim on multiple files, by using the argdo command. So your command turns into:
vim -c "argdo source script.vim | w" A.txt B.txt C.txt D.txt
Finally, if you want to quit Vim after running your script on every file, just add another command to quit:
vim -c "argdo source script.vim | w" -c "qa" A.txt B.txt C.txt D.txt

Try the following syntax:
ex foo.txt <<-EOF
g/^\s*$/d
%s/^/[/
%s/\(.*\)\(\s\+\)\(.*\)/\3\2\1
%s/$/]/
%s/design/test/
wq " Update changes and quit.
EOF
The ex command is equivalent to vim -E. Add -V1 for verbose output.
Alternative one-liner syntax is for example:
ex +"g/^\s*$/d" +"%s/^/[/" +"%s/design/test/" -cwq foo.txt
To load commands from the file, use -s cmds.vim.
You can also use shebang for Vim to parse the file from the argument.
For more examples, see:
How to edit files non-interactively (e.g. in pipeline)?
BashFAQ/021

JimR and Ingo have provided excellent answers for your use case.
Just to add one more way to do it, however, you could use my vimrunner plugin to script the interaction in ruby: https://github.com/AndrewRadev/vimrunner.
Example:
vim = Vimrunner.start
vim.edit "file.txt"
vim.insert "Hello"
vim.write
vim.kill
This can be useful for more complicated interactions, since you get the full power of a programming language.

Use vim -s ... to script not only colon commands, but also normal-mode commands such as = for formatting:
Create a file with all keystrokes that you want vim to execute.
Run vim -s SCRIPT-FILE FILE-TO-EDIT
For example: Let's say you want to use vim's = command to re-indent all the lines of myfile.html. First, using vim itself, make a file named myscript that has this:
gg=G:wq
(gg moves to the top of the file; =G re-indents from the current location to the end of the file; :wq<Enter> saves the file and exits.)
Then, run this to have vim launch, edit myfile.html, and save it:
vim -s myscript myfile.html

Related

command substitution not working in alias?

I wanted to make an alias for launching a vim session with all the c/header/makefiles, etc loaded into the buffer.
shopt -s extglob
alias vimc="files=$(ls -A *.?(c|h|mk|[1-9]) .gitconfig [mM]akefile 2>/dev/null); [[ -z $files ]] || vim $files"
When I run the command enclosed within the quotations from the shell, it works but when run as the alias itself, it does not. Running vimc, causes vim to launch only in the first matched file(which happens to be the Makefile) and the other files(names) are executed as commands for some reason(of course unsuccessfully). I tried fiddling around and it seems that the command substitution introduces the problem. Because running only the ls produces expected output.
I cannot use xargs with vim because it breaks the terminal display.
Can anyone explain what might be causing this ?
Here is some output:
$ ls
Makefile readme main.1 main.c header.h config.mk
$ vimc
main.1: command not found
main.c: command not found
.gitignore: command not found
header.h: command not found
config.mk: command not found
On an related note, would it be possible to do what I intend to do above in a "single line", i.e without storing it into a variable files and checking to see if it is empty, using only the output stream from ls?

copying bash completion (without copying the actual code)

Suppose I have a command git-local (it could be a Bash function or a binary in /usr/local/bin) and suppose I would like git-local to have the same tab completion as the command git has. Finally, suppose that I'm efficient (read lazy) and I don't want to go find the code that the git commmand uses to manually copy over and bloat my .bashrc (or whatever external file I paste it in and the source). Is there a simple way I can have git-local use the same autocompletion as git?
8.7 Programmable Completion Builtins:
If the -p option is supplied, or if no options are supplied, existing completion specifications are printed in a way that allows them to be reused as input.
Something like
$(complete -p git | awk '$NF="git-local"')
maybe?
E.g.:
$ complete -p foobar
-bash: complete: foobar: no completion specification
$ complete -p traceroute
complete -F _known_hosts traceroute
$ $(complete -p traceroute | awk '$NF="foobar"')
$ complete -p foobar
complete -F _known_hosts foobar

Vim shell commands (c flag) behaving differently in bash

If I run this in the terminal (Mac OSX):
vim dump.md -c 'vsplit poa.md' -c 'split one.md' -c ':wincmd h'
It works great (creates three windows with those files).
If I have it as part of a bash script, there's a problem. After doing what it's supposed to do, it leaves me with this in the vim status line:
1:dump.md 2:poa.md 3:one.md
1:dump.md 2:poa.md 3:one.md
Press ENTER or type command to continue
I have to press ENTER 6 times to clear the status line and return to editing (each ENTER outputs the above again).
Anyway around this?
Also, not sure if this is significant, but vim is aliased to mvim (MacVim).
EDIT
Here is the bash script, although I don't think it matters I've run the whole script separately before manually putting the vim dump.md... command it, and it works fine.
13
12 #!/bin/bash
11
10 cd ~/Dropbox/Journal/dumps/
9
8 LAST=`ls -tAF | grep '/$' | head -1`
7 NEW=dump-$(date +"%Y-%m-%d_%T")
6
5 mkdir $NEW
4 cd $NEW
3
2 cp ../$LAST/poa.md ./poa.md
1
14 vim dump.md -c 'vsplit poa.md' -c 'split one.md' -c ':wincmd h'
1
At first:
ls -al
And see just your file is existed...If your file is test make sure vim doesn't cread such files:
.test OR test~ OR something like these
Then :
vim file
Do your changes...
After that close it by saying:
:wq!
Then try to open it again...The problem should be solved
UPDATE
try this one:
ls -a | grep swp
if there was a file like : .script.swp or something like these you should remove them...
Your problem would be solved...You should see if your script it existed with .script.... and you should remove it...
If you can't find files start with . try to move all files(scripts) to another directory then work with them..and use :q! to quite and use :wq! to save and quite...

Executing Bash functions from within Vim - how can I do it? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Commands executed from vim are not recognizing bash command aliases
I have the following function in my ~/.bashrc file (on my Ubunut box)
# convert tex to pdf, no bib
function t2p {
latex $1 && latex $1 && dvips $1 -o $1.ps && ps2pdf $1.ps && rm -f $1.dvi }
# convert tex to pdf, with bib
function tb2p {
latex $1 && bibtex $1 && latex $1 && latex $1 && dvips $1 -o $1.ps && ps2pdf $1.ps && rm -f $1.dvi }
For example, to convert a tex file f.tex to a pdf file and bibtex it in the right order, I call tb2p f. This works very well if I'm in a Bash terminal. However, to get to the Bash prompt from within Vim I actually have to execute the command :sh first.
To simplify the above procedure I tried to execute the functions t2p and tb2p inside Vim by :!t2p f. Vim then tells me that it cannot find the function t2p. I did some Googling and read that I should put these functions into the file /etc/bash.bashrc to make them visible to Vim. Unfortunately, this didn't work in my case. Vim still doesn't know about the function.
At the end of the day I would like to be able to call my functions from within Vim by using a keyboard shortcut. My questions are therefore as follows:
How can I let Vim know about ~/.bashrc functions?
How do I setup a keyboard shortcut in ~/.vimrc for a function in ~/.bashrc?
Thank you very, very much.
Try using :!bash -c t2p in Vim. If your alias is limited to interactive shells also add the -i flag.
Vim runs bash commands with the -c argument, which makes the shell non-interactive and non-login, and that bypasses reading the startup files.
You can override this with an environment variable.
From man bash:
When bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV
in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to
read and execute. Bash behaves as if the following command were executed:
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
but the value of the PATH variable is not used to search for the file name.
Alternatively, vim itself can be asked to run command shells as login shells. From vim :help shell we get the following:
On Unix the command normally runs in a non-interactive
shell. If you want an interactive shell to be used
(to use aliases) set 'shellcmdflag' to "-ic".
For Win32 also see |:!start|.
Try adding set shell=bash\ --login to your .vimrc.
To convert the current file, type :!tb2p %, the % will be expanded by Vim for you when executing the script.
Once it's working, you can add a mapping to make the whole process even faster:
nnoremap <F12> :!tb2p %<CR>
You can always define your functions in a separate file and put that file in a folder
in your PATH environment variable.
In my case my personal functions that I would use go to ~/bin
In your case for t2p:
create a file t2p in ~/bin with the contents:
#!/bin/bash
# convert tex to pdf, no bib
latex $1 && latex $1 && dvips $1 -o $1.ps && ps2pdf $1.ps && rm -f $1.dvi
then make the file executable:
> chmod +x t2p
make sure ~/bin is in your path, by putting the following in your ~/.bashrc:
export PATH=${HOME}/bin:${PATH}

xargs with command that open editor leaves shell in weird state

I tried to make an alias for committing several different git projects. I tried something like
cat projectPaths | \
xargs -I project git --git-dir=project/.git --work-tree=project commit -a
where projectPaths is a file containing the paths to all the projects I want to commit. This seems to work for the most part, firing up vi in sequence for each project so that I can write a commit msg for it. I do, however, get a msg:
"Vim: Warning: Input is not from a terminal"
and afterward my terminal is weird: it doesn't show the text I type and doesn't seem to output any newlines. When I enter "reset" things pretty much back to normal, but clearly I'm doing something wrong.
Is there some way to get the same behavior without messing up my shell?
Thanks!
Using the simpler example of
ls *.h | xargs vim
here are a few ways to fix the problem:
xargs -a <( ls *.h ) vim
or
vim $( ls *.h | xargs )
or
ls *.h | xargs -o vim
The first example uses the xargs -a (--arg-file) flag which tells xargs to take its input from a file rather than standard input. The file we give it in this case is a bash process substitution rather than a regular file.
Process substitution takes the output of the command contained in <( ) places it in a filedescriptor and then substitutes the filedescriptor, in this case the substituted command would be something like xargs -a /dev/fd/63 vim.
The second command uses command substitution, the commands are executed in a subshell, and their stdout data is substituted.
The third command uses the xargs --open-tty (-o) flag, which the man page describes thusly:
Reopen stdin as /dev/tty in the child process before executing the
command. This is useful if you want xargs to run an interactive
application.
If you do use it the old way and want to get your terminal to behave again you can use the reset command.
The problem is that since you're running xargs (and hence git and hence vim) in a pipeline, its stdin is taken from the output of cat projectPaths rather than the terminal; this is confusing vim. Fortunately, the solution is simple: add the -o flag to xargs, and it'll start git (and hence vim) with input from /dev/tty, instead of its own stdin.
The man page for GNU xargs shows a similar command for emacs:
xargs sh -c 'emacs "$#" < /dev/tty' emacs
(in this command, the second "emacs" is the "dummy string" that wisbucky refers to in a comment to this answer)
and says this:
Launches the minimum number of copies of Emacs needed, one after the
other, to edit the files listed on xargs' standard input. This example
achieves the same effect as BSD's -o option, but in a more flexible and
portable way.
Another thing to try is using -a instead of cat:
xargs -a projectPaths -I project git --git-dir=project/.git --work-tree=project commit -a
or some combination of the two.
If you have GNU Parallel http://www.gnu.org/software/parallel/ installed you should be able to do this:
cat projectPaths |
parallel -uj1 git --git-dir={}/.git --work-tree={} commit -a
In general this works too:
cat filelist | parallel -Xuj1 $EDITOR
in case you want to edit more than one file at a time (and you have set $EDITOR to your favorite editor).
-o for xargs (as mentioned elsewhere) only works for some versions of xargs (notably it does not work for GNU xargs).
Watch the intro video to learn more about GNU Parallel http://www.youtube.com/watch?v=OpaiGYxkSuQ
Interesting! I see the exact same behaviour on Mac as well, doing something as simple as:
ls *.h | xargs vim
Apparently, it is a problem with vim:
http://talideon.com/weblog/2007/03/xargs-vim.cfm

Resources