Capture the output of an interactive script in vim - vim

I have a an interactive Perl script, which prints prompts to STDERR and reads lines from STDIN. The final output of this script is an IP address, printed to STDOUT. Here's a numpty version of such a script as an example.
my #pieces;
for (1..4) {
print STDERR "please enter piece $_ of the IP:"; chomp(my $in = <>);
push #pieces, $in;
}
print join '.', #pieces;
print "\n";
I use the vim-fireplace vim plugin. This plugin has a feature where I can say:
:Connect nrepl://127.0.0.1:9999
I want to know how to configure vim so that when I issue a particular command, let's say:
:InteractiveConnect
it will do the following:
Run the Perl script, allowing me to enter 4 pieces of the IP address.
Capture the IP address output by the Perl script.
Interpolate the IP address into the :Connect command
Run the :Connect command.
A bit more info based on some of the responses:
If I call this script using:
:!/path/to/myscript.pl
Then it executes fine and I am able to see the result from it printed in the vim window, followed by
Press ENTER or type command to continue
If the output of the script is being saved in some buffer after execution via !, is it possible to get access to that buffer in vimscript and just capture the bit I want (the last line) with a regex?

Okay, there's probably a more elegant way to do this, but how about this:
function! <SID>InteractiveConnect()
let tempfile=tempname()
exe '!/path/to/your/script.pl >' . shellescape(tempfile)
try
exe 'Connect nrepl://' . readfile(tempfile, '', -1)[0]
finally
call delete(tempfile)
endtry
endfunction
command! -nargs=0 InteractiveConnect call <SID>InteractiveConnect()
This creates a temporary file, writes to it with the script (using system() doesn't work because it doesn't wait for input), reads the last line in the tempfile to the Connect command, and then finally deletes the tempfile.

Maybe something like:
exec 'Connect nrepl://' . matchstr(system('your/script.pl'), '^.\+\%$')
(Untested.) This runs the script using system() then matches the output against the regular expression ^.\+\%$, (where \%$ means end-of-file; if your file is terminated with a newline, an additional \n might be neccessary before it) and feeds the matched str to the Connect command. .

Related

VI read line not giving desired output

I have a file called test, I open it using vi as such:
vi test
Now I want to insert a line through a shell command, for simplicity I use a printf:
:r! printf %s hello
However the line that is entered is
tests
i.e. the name of the file with a s appended.
If I enter the same command in terminal directly, it works fine.
What I want to do is ultimately be able to encode a string in base64 and enter it on the same line as where my cursor is in vi, so that I won't have to copy the string in a separate terminal, encode it, and copy it back into vi. How can I do this? What am I doing wrong?
The first stage of processing a command line in vim is expanding it. % is expanded to the name of the current file — test in your case. %s is expanded to tests.
To avoid expanding protect the special character with a backslash:
:r! printf \%s hello

How to write a string in a file using vim editor from command line

I want to create a new file using vi editor from command line and add a string to it multiple times say 100. Using vi -S command.script file.txt is supposed to do the trick where a new file file.txt will be created and the commands given in command.script file can write to this file. My command.script contains
:%100a hello world
:wq
But its's not working, what I am doing wrong?
If you interactively execute :%100a hello world in a Vim session, you'll get E488: Trailing characters. Looking up :help :a:
:{range}a[ppend][!] Insert several lines of text below the specified
line. If the {range} is missing, the text will be
inserted after the current line. [...]
These two commands will keep on asking for lines, until you type a line
containing only a ".".
tells you that the text has to be put in following lines (and concluded by a line with only a . character).
Or did you mean to use the normal mode a command? (That one takes a [count] to multiply; your %100 range is wrong, too!)
You can also use the low-level function append(), repeating the string with repeat().
summary
$append
hello world
[...]
hello world
.
execute "$normal! 100ahello world\<CR>"
" Easier with o instead of a:
$normal! 100ohello world
call append('$', repeat(['hello world'], 100))
non-Vim alternatives
But honestly, if that is your real use case (and not just a simplified toy example), you don't need Vim at all for this. Here's one example for the Bash shell:
$ for i in $(seq 100); do echo "hello world" >> file.txt; done

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 --- Read from an External Command, Inserting Right Where the Cursor Is

:r !program opens a new line, inserts my program's output and then inserts a line after it.
I simply want to insert the output right where the cursor is without that additional mess.
I figured I can:
Run a before macro
mai^M^[`a
"Mark where I'm at, insert a line and go back
Run my command
:r !echo -ne "line1\nline2\nline3"
Run an after macro (cleanup the lines)
$mb:j!^M`a:j!^M`b
"Go to the end of inserted outpu
"Mark it b
"Join with the next line
"Go to the first mark
"Delete the inserted newline with :j!
"Go to the second mark
How can I combine this into a single command?
I'd like to be able to do:
:Readhere !echo -ne "line1\nline2\nline3"
where :Readhere would be my custom command.
This might do what you want. (You don't need the !)
command! -nargs=1 ReadHere exec 'normal! i' . system(<q-args>)
This creates a command called ReadHere that takes everything as a quoted argument and passes it directly to the system command. Then we use exec to insert everything in normal mode. (This might not be robust enough)
Example: Starting buffer is
one two three
Running :ReadHere echo -ne "line1\nline2\nline3" where the cursor is on the w produces
one tline1
line2
line3wo three

Vim write temporary info as :write does

I have a macro which calls a function I defined in my vimrc
:function! DoStuff()
:!mycommand
:end
map <C-p>i :call DoStuff()<CR>
When I press the macro keys I get right a shell with the output of mycommand and it works fine but I would like improve that.
I noticed that when I write a file (:w) a short message is displayed for short time in the command bar which says "File X written", I want to achieve the same result, when macro sequence is pressed I want check if the command went fine (checking the return code) and then display a message (such "Ok" or "Not Okay") as the :write command does.
Any ideas ?
Write external command output to a temporary file
via Vimscript functions
First, capture the output of the external command in a variable, whose contents can then be written to a (temporary) file via writefile().
:let output = system('mycommand')
:call writefile(output, 'path/to/tempfile')
via scratch buffer
If you need to apply arbitrary commands to the captured contents, or need Vim to handle different file encodings, you have to use a buffer:
:new path/to/tempfile
:0read !mycommand
:write
:bdelete
via file redirection
This is the simplest approach when you need the output as-is:
:!mycommand > path/to/tempfile
Check for command success and notify
After external command execution, the special variable v:shell_error contains exit status of the last shell command. You can test that to report a message to the user:
!mycommand
if v:shell_error != 0
echohl ErrorMsg
echomsg "The command failed"
echohl None
else
echomsg "The command succeeded"
endif
If you've used system() to execute the command, you also have the error message returned by it, and can include that in the notification.

Resources