Redirect ex command to STDOUT in vim - vim

I'm trying to craft a command to dump vim's highlighting information to STDOUT. I can write successfully to a file like this:
vim +'redir >outfile' +'hi' +'redir END' +'q'
After this, outfile contains exactly what I want. But I'd rather output to STDOUT to pipe to a command which converts that highlighting info to CSS.
This approach tries to redirect the command to a register, write to the current buffer, then send that output to tee.
vim -E +'redir #a' +'silent! hi' +'redir END' +'put a' +'w !tee' +'q!' > outfile
This is pretty close, but outputs a leading paging message (255 more lines...) and some ANSI escape crap at the end. Setting nomore did not squelch the message for me. I'm looking to send the exact output sent to outfile in the first command to STDOUT.

Vim supports outputting to stderr, which we can redirect to stdout to solve your problem. Careful though, this feature is intended only for debugging purposes, so it has a few edges. Here's a short sketch of how you could do this.
First, we will be running Vim in silent or batch mode, which is enabled with the -e -s flags (:h -s-ex). We disable swap files -n because they will be a bother if we need to kill Vim when it gets stuck.
Instead of passing Ex commands as command line arguments we source a script file with -S. Create a file hi.vim with the following contents:
verbose highlight
:verbose is necessary to make Vim output the :highlight message to stderr. Let's see what we have so far:
$ vim -n -e -s -S hi.vim
Don't run this yet or you'll get stuck in the darkness of a headless Vim!
Add a :quit command and redirect stderr to stdout:
$ vim -n -e -s -S hi.vim +quit 2>&1
Voilà! Now pipe this mess into any file or utility at your heart's desire.
There's a very thorough wiki article on this topic, "Vim as a system interpreter for vimscript".
Finer points: Due to how Vim interacts with the terminal environment it can't write proper unix LF line endings in batch mode output. Instead it writes line endings which look like CRLF line endings.
Consider adding a filter to get rid of those:
$ vim -n -e -s -S hi.vim +quit 2>&1 | tr -d '\r' | my-css-util
This answers the general question of how to "Redirect ex command to STDOUT in vim", but it doesn't necessarily work for your :hi problem, because of this constraint caused by the -e -s flags:
'term' and $TERM are not used.

If you're working with Unix compatible environment, you can always use a special file, e.g.:
ex +"redir>>/dev/stdout | hi | redir END" -scq!
This will print any Vi/Ex command into standard output which you can parse further.

I ran into this by mistake while running your first command. The second time I ran it, it went to stdout. I guess this is because the file already existed. So you can try (sending to something that already exists):
vim +'redir >/dev/null' +'hi' +'redir END' +'q'

Related

How to redirect vim's output to terminal?

I'm currently writing a small bash script to create a pre-filled file at a specific location. To do so, my bash script call vim and execute a vim script.
In this vim script, I want to ask to the user if the location where the created file is going to be saved is the correct one. The user then enter yes or no.
To do so, I'm using this set of command:
call inputsave()
let name = input('Is it the correct location ? y/n')
call inputrestore()
this input function works fine when I'm in gvim or vim. But from a script, nothing is displayed is the terminal where I launched my bash script.
Is there a way to redirect outputs to my terminal ?
I found :redir > {file} but this is obviously redirecting all vim outputs to a file which not interactive.
I also managed to echo something int the terminal by using this:
let tmp = "!echo what I want to display"
execute tmp
unlet tmp
but again this is only to display something. I won't be able to enter an input
my bash script:
#!/bin/bash
touch tmp.txt #file used to pass argument to vim
echo "$1" >> tmp.txt #append argument to the end of the txt file
vim -e tmp.txt <create_new_file.vim #launch vim and execute the scrip inside create_new_file.vim
rm tmp.txt
echo "new file created"
the create_new_file.vim basically just call my function CreateFile(_name) located in my .vimrc. This is this function who call inputsave(), input() and inputrestore().
You're feeding commands directly into Vim via standard input; this way, Vim has no way to interact with you (the user), and this use of Vim is atypical and therefore discouraged.
Alternatives
Unless you really need special Vim capabilities, you're probably better off using non-interactive tools like sed, awk, or Perl / Python / Ruby / your favorite scripting language here.
That said, you can use Vim non-interactively:
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 "create_new_file.vim" tmp.txt
This should allow you to interact with Vim while following a script of commands.
Here's a summary of the used (or useful) arguments:
-N -u NONE Do not load vimrc and plugins, alternatively:
--noplugin Do not load plugins.
-n No swapfile.
-i NONE Ignore the |viminfo| file (to avoid disturbing the
user's settings).
-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.

store all the data in terminal to text file by tee command or equivalent tool

I learnt that a tee command will store the STDOUT to a file as well as outputs to terminal.
But, here the problem is every time I have to give tee command, for every command I give.
Is there any way or tool in linux, so that what ever I run in terminal, it should store the command as well as output. (I used tee command in MySQL, where it will store all the commands and outputs to a file of that entire session. I am expecting a tool similar to this.)
Edit:
When I run script -a log.txt, I see ^M characters as well as ^[ and ^] characters in log.txt file. I used various dos2unix, :set ff=unix, :set ff=dos commands, but they didn't helped me in removing these ^[, ^] characters.
Is there any method, I can directly get the plain text file (with out these extra chars).
OS: RHEL 5
You can use script command which writes everything on file
script -f log.txt
you could use aliases like such alias ls="ls;echo ls >>log" so every time you run ls it runs echo ls >>log too.
But script would probably be better in this case, just dont go into vi while you are in script.

Execute an external command on startup

I use vim regularily for all my needs. On very rare occasions I need to open binary files via a hex editor look, and for that I start vim on the file and then run it through xxd via the command: %!xxd
My question is, how can I have my command line open a file directly in this manner, if the option exists? something like typing: gvimbin <file> and then it opens in the right manner.
Edit: to be clear, I am looking for a complete solution that allows running vim exec commands on startup.
You can execute commands after Vim startup by passing them via -c:
$ gvim -c '%!xxd' file.bin
This can even be simplified so that when Vim is started in binary mode (-b argument), it'll automatically convert the file. Put the following into your ~/.vimrc:
if &binary
%!xxd
endif
and start with:
$ gvim -b file.bin
Also have a look at the hexman.vim - Simpler Hex viewing and editing plugin; it makes it easier to deal with hexdumps.
Like Felix say:
xxd <file> | vim -
You can put this into script for example vimxxd:
#!/bin/sh
xxd $1 | vim -
and use like: vimxxd file.txt

What is a way to read man pages in Vim without using temporary files

I want to be able to read man pages in Vim.
For some reason, it seems that Vim isn't able to read the output of programs through piping. E.g (man ls) | vi doesn't seem to work, bonus points for somebody who can explain why.
To get around this, I've been using the following little script:
tempo = `mktemp`
man $1 > $tempo ; vi $tempo
This script uses temporary files which I guess work fine, but I was wondering if there was a good way to read man pages in Vim without resorting to creating temporary files
Vim includes a man page viewer, :Man, in its runtime files.
Put this line in your vimrc:
runtime! ftplugin/man.vim
Now you can read syntax-highlighted man pages inside Vim by running :Man. For example:
:Man 3 printf
Even better, you can just place your cursor on a word in the buffer and press <Leader>K (\K) to see the man page for that word.
See :h find-manpage for complete usage and installation instructions.
For some reason, it seems that vim isn't able to read the output of programs through piping […]
According to the man-page, you need to specify a file of - to get it to read from standard input; so:
man ls | vi -
If that doesn't work, you might try using process substitution:
vi <(man $1)
which creates a sort of pseudo-file and passes it to vi.
On my system (Mac OS X), I found that the above left control characters in the output. Instead I used:
export MANPAGER="col -b | vim -MR - "
then just e.g.
man vim
The vim options turn off modifying the buffer and make it read-only. This stops vim complaining if you try to exit with ":q" (you can use :q! of course, but you might as well set the options).
This is also handy for general use - I have the following. The -c command names the buffer, just for completeness.
alias vimpager="vim -MR -c 'file [stdin]' -"
Here is what I did: I've made a function in my .bashrc:
vman() { vim <(man $1); }
When I call vman this automatically calls Vim showing the man page. It works great.
Your example code is wrong.
tempo=`mktemp`
man $1 > $tempo; vi $tempo
But you really only need
man $1 | vi -
By default vim reads vimscripts (=vim commands), not input files, from stdin. That is why you cannot directly pipe man output to vim; as others have mentioned you have to use vim - to make vim read from stdin.
However piping vimscripts can be useful too:
vim test.txt <<EOF
:%s/[aiueo]/X/g
:wq! output.txt
EOF
The above will use vim to open test.txt, replace all vowels with X, write the results to output.txt, and quit (ignoring changes to the original file). It uses a here document but you can of course put the vim commands in a file and use vim test.txt < myscript or cat myscript | vim test.txt to achieve the same result.
I suspect the reason they did it this way was that you can open multiple input files but only execute one script. If input was read from stdin by default, you could only read one buffer that way.
I combined others answers, I am using
vman() {
export MANPAGER="col -b" # for FreeBSD/MacOS
# Make it read-only
eval 'man $# | vim -MR +"set filetype=man" -'
unset MANPAGER
}
Usage:
vman ls
You also can press shift-k on your c function to print the man page
I have a better solution, the one that I used, it is like this:
/bin/sh -c "unset PAGER;col -b -x | vim -R -c 'set ft=man nomod nolist' -c 'map q :q<CR>' -c 'map <SPACE> <C-D>' -c 'map b <C-U>' -c 'nmap K :Man <C-R>=expand(\"<cword>\")<CR><CR>' -"
Hope you'll enjoy it.
You can always use info command for info pages and do info {cmd} | vim.
Source.
A lot of good answers, with respect to plugins it's worth to add that vim-man* provides a set of convenience functions to open and read man pages:
Viewing man pages, as per docs.
:Man printf - open printf(1) man page in a split
:Vman 3 putc - open putc(3) man page in a vertical split
:Man pri<Tab> -
command completion for man page names
* Available on GitHub: https://github.com/vim-utils/vim-man.

How do I launch an editor from a shell script?

I would like my tcsh script to launch an editor (e.g., vi, emacs):
#!/bin/tcsh
vi my_file
This starts up vi with my_file but first displays a warning "Vim: Warning: Output is not to a terminal" and my keystrokes don't appear on the screen. After I kill vi, my terminal window is messed up (no newlines), requiring a "reset". I tried "emacs -nw", "xemacs -nw", and pico with similar results. "xemacs" works but launches a separate window. I want to reuse the same terminal window.
Is there a way to launch an editor from a script so that it reuses the same terminal window?
I answered my own question! You have to redirect terminal input and output:
#!/bin/tcsh
vi my_file < `tty` > `tty`
The reason you're getting the error is that when you start a shell in your environment, it's starting in a subshell that has STDIN and STDOUT not connected to a TTY — probably because this is in something like a pipeline. When you redirect, you're opening a new connection directly to the device. So, for example, your command line turns
$ vi < `tty` > `tty`
into
$ vi < /dev/ttys000 > /dev/ttys000
So you're not really using your old STDIN/STDOUT, you're creating two new files and mapping them to your vi process's STDIN/STDOUT.
Now, tell us what you're doing with this and we'll tell you how to avoid this kludge.
I wanted to do something similar. I wanted an alias that would find the last file I was working on, and open it in vi(1) for editing. Anyway, I couldn't figure out how to do it as a readable alias (in tcsh) so I just created an ugly shell script (csh because I'm old) instead:
#!/bin/csh
set DIR = "~/www/TooMuchRock/shows/"
set file = $DIR`ls -t $DIR | head -1`
set tty = `tty`
vi $file <$tty >$tty
(1) kraftwerk:bin> which vi
vi: aliased to /usr/local/bin/vim -u ~/.exrc
Absolutely. :-)
Write your script and have it call the EDITOR environment variable, which you will have set to "emacsclient". Then start up Emacs, execute M-x server-start, switch to a shell buffer (M-x shell) and execute your script. Emacsclient will pop up the thing to be edited and C-x # will act as a "done" command and take you back to your script with edits completed or aborted, as you choose.
Enjoy.
Edit: I meant to add that these days Emacs IS my terminal program. I have dozens of shell buffers and never have to worry about losing output and can use all the power of Emacs to manipulate and analyse the terminal output. And have Emacs scripts generate input to the shells. Awesome actually. For example, watching Tomcat output scroll by in a shell buffer while editing sources or processing mail or doing most any Emacs thing is very convenient. When a Tomcat stack trace appears I can quickly respond to it.
Had the same trouble with 'pinfo' in a shell script 'while' loop. The line can be used in the script, it uses 'ps' to find the tty of the current process number, "$$", and stores that tty in $KEY_TTY:
KEY_TTY=/dev/`ps | grep $$ | tr -s '[:blank:]' | cut -d " " -f 3`
Later in the script, just call the tty-only proggie, with $KEY_TTY as input, in my case it was:
pinfo -m $s $page < $KEY_TTY
For 'vi' it'd be:
vi $a < $KEY_TTY > $KEY_TTY
The advantage is that the script as a whole can still accept STDIN input, and 'vi' (or whatever) should work fine -- without having to remember to set any environmental variables before running the script.
Set your terminal tty to a variable, and then redirect the editor i/o through that variable.
In your script:
#!/bin/sh
ls | while read a; do vi $a < $MYTTY >$MYTTY; done
And then execute the script with:
$ MYTTY=`tty` ./myscript >/tmp/log
I was able to get the desired behavior under bash+Cygwin+Terminator:
#!/bin/bash
vim foo
Run the script, vim loads, no error messages, behaves as normal. There are undoubtedly dozens of variations between our setups, however, so I can't hazard a guess as to what makes the difference. I'm curious what it is, but you got it working, which is the important part.

Resources