Modify textblock by external script in vim [duplicate] - bash

This question already has answers here:
Replace vim selection with output of shell command
(4 answers)
Closed 1 year ago.
While editing files I often encounter the following task: I have a standalone script that takes input and outputs a modified version of it (could be sort, could be a sed script, could be a more complicated python script) and I want to run it on a block of text within vim.
This answer explains how to feed a text block to an external script and I assume that one can similarly read a script's output into a file using :r. But how can I do both: feed text out of vim to an external script and its output back into vim?

You can filter a block of text in vim using the normal mode commant
!{motion}. For instance, to use the external sort tool on the text below
(ignoring the fact that vim has it's own sort for now):
b
a
d
c
use !ip (external command !, inside paragraph), which will take you to
the command line with a prepopulated command line prompt (mine looked like
:.,.+4!) where you simply type whatever external tool (e.g. :.,.+4!sort)
you want to send the text to as stdin. The resulting stout will replace the
selected lines:
a
b
c
d

Related

How do I edit current shell command without executing it?

There seems to be quite a lot of information on how to edit and execute a command using your editor using "edit-and-execute-command (C-x C-e)", but what I would like to achieve is take the current shell command, apply certain filtering (using a script) and then return it to prompt for further approval/manual changes before execution. Is this possible with bash?
Latest update based on my experience
The part 0"+y$dd in the following mapping is really something that you should carefully think about and tailor it to your taste/workflow/experience.
For instance, very frequently I've found myself ending up with multiple lines in the buffer, where I only want to execute the one the cursor is on; in this case I can use 0"+y$dd:%d<CR> instead of 0"+y$dd.
And this is just one of the possible scenarios.
Final answer for those who like vim
Set vim as your EDITOR/VISUAL, so that when editing a command line, you will use vim to edit it.
Put au BufEnter /tmp/bash-fc.* nn <Leader>d 0"+y$dd:wq<CR> in your ~/.vimrc file to map Leaderd (which you will rarely use when editing a command) to the action "delete the current line into the + register without the trailing EOL".
you can use either the + or the * register in the mapping above; the ways to paste into the terminal will likely differ; you need the +clipboard option for these registers to be available.
When finished editing a command in the vim editor, hit EscapeLeaderd.
Paste the clipboard into the terminal (this is terminal-dependent).
Original answer
I often need to do the same, and I do it as follows. (I normally use the set -o vi in bash, so points 1 and 2 in the following are different if you use set -o emacs, the default; based on your question it looks like points 1 and 2 are unified in Ctrl+x followed by Ctrl+e, which is harder to type, imho.)
hit Escape to be in normal mode,
hit v to enter the editor to edit the command,
edit the command as I like,
(This is where you ask the question.)
hit Escape0"+y$dd:wq,
Note: 0"+y$, not simply "+yy, as the latter would copy the newline too, and this would result in executing the command upon pasting it in the command line,
paste the clipboard on the command line
how to do this depends on the terminal you are using, I guess; I hit Ctrl+Alt+v in URxvt.
proceed to approval/manual edit.
Clearly this is just a workaround, consisting in copying the edited command into the clipboard before deleting the whole command, so that nothing gets executed upon exiting the editor; however it's the best I can get for myself.
Update
As my EDITOR (and VISUAL) is equal to vim, when I edit the command, I edit it in vim.
In this respect, I have noticed that the buffer is named /tmp/bash-fc.random, where random is a 6-characters alphanumeric random string.
This gives space to a lot of possiblities, if you use vim as your editor, as you can define some mapping in your .vimrc to execute the whole sequence Escape0"+y$dd:wq. For instance, one command that you'd rarely use when editing a command line is Leaderd; therefore you can put the following mapping in your .vimrc file
au BufEnter /tmp/bash-fc.* nn <Leader>d 0"+y$dd:wq<CR>
so that step 4 in the above recipe becomes
hit EscapeLeaderd
It's not possible to do that in Bash/readline but it's possible in zsh
using edit-command-line command:
darkstar% autoload edit-command-line; zle -N edit-command-line
darkstar% bindkey "^X^E" edit-command-line
Now press Control-x Control-e to open your editor, edit line, leave the editor - you will see the updated command line but it will not be executed automatically.
Now that I think about it, maybe a variation of what #kenorb suggested in a comment is the best workaround (as it seems no solution exists), if we want to stick to bash.
What you can do is prepend a # (the comment character in bash) to the command, rather than echo. Then when you exit the editor, the command will be ineffective, and you will only have to press arrow up (or k, if you use set -o vi), remove the # and confirming.
Note that this strategy adds just a few keystrokes, so it can be fairly efficient, depending on your typing level.
These pieces might get you closer:
a) replace the the normal binding for newline newline (ctrl-M)
bind -x '"\C-M":date"
b) grab the current line from the history using !#
replace date with whatever script you want.
c) edit manually
d) if necessary, somehow tie in !!:p which prints the new command to the command line but does not execute it, thus letting you manually edit it.
e) using ctrl-J submit edited command rather than a newline
or they might not ....
There is an option in bash to modify command from history without executing it. I'm not sure it it's possible to use script for this, doesn't seem to be likely. Although, you can make modifications using history modifiers.
Enable option histverify to prevent execution of modified command
Use chain of modifiers to change last command
Use "!!" to put your result to command line for final edit
Here is how it looks:
$ shopt -s histverify
$ ls *.sh
script1.sh script2.sh script3.sh script-return.sh
$ !!:s/*/script1/:p
ls script1.sh
$ !!:s/1/2/:p
ls script2.sh
$ !!
$ ls script2.sh
script2.sh
I'd like to point you to the Composure framework for Bash (I'm not affiliated with it): https://github.com/erichs/composure
It provides draft and revise functions that sound like they could help with what you're trying to do. Here's a (long) quote from the project's readme file:
Composure helps by letting you quickly draft simple shell functions,
breaking down your long pipe filters and complex commands into
readable and reusable chunks.
Draft first, ask questions later
Once you've crafted your gem of a command, don't throw it away! Use
draft () and give it a good name. This stores your last command as a
function you can reuse later. Think of it like a rough draft.
$ cat servers.txt
bashful: up
doc: down
up-arrow
$ cat servers.txt | grep down
doc: down
$ draft finddown
$ finddown | mail -s "down server(s)" admin#here.com
Revise, revise, revise!
Now that you've got a minimal shell function, you may want to make it
better through refactoring and revision. Use the revise () command
to revise your shell function in your favorite editor.
generalize functions with input parameters
add or remove functionality
add supporting metadata for documentation
$ revise finddown
finddown ()
{
about finds servers marked 'down' in text file
group admin
cat $1 | grep down
}
$ finddown servers.txt
doc: down
It does not seem possible with a keyboard shortcut, at least:
$ bind -P | grep -e command -e edit
complete-command can be found on "\e!".
edit-and-execute-command can be found on "\C-x\C-e".
emacs-editing-mode is not bound to any keys
possible-command-completions can be found on "\C-x!".
vi-editing-mode is not bound to any keys
This can be done in native bash using readline specifically READLINE_LINE and READLINE_POINT variables. I use this functionality all the time though not through vim, you would need to get the value of $selected from your vim command and if not empty it takes your original line + your input and replaces your original line with the combination without executing. output as a variable
_main() {
selected="$(__coms_select__ "$#")"
origonal_text=$READLINE_LINE READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
}
bind -m emacs-standard -x '"\C-e": _main '
bind -m vi-command -x '"\C-e": _main '
bind -m vi-insert -x '"\C-e": _main '
Edit
Just remembered these two utilities that will let you do this as well.
Vipe allows you to run your editor in the middle of a unix pipeline and edit the data that is being piped between programs.
vp, up, vipe, Neomux (upgrade of nvim terminal) you can do some pretty neat throwing buffers between the terminal and split window.
and Athame (full vim on the command line)
https://github.com/ardagnir/athame
careful with that one though plugins work on the cli and it can get funky if you got tons of plugins

Can you execute a shell command from that file by reading it?

Given
unix
file with regular text and a unix command , and other regular text
-
Is there a way to write that command so that when we do a less or a cat (simple reading) or some innocent operation on that file, we actually trigger the command?

How can I visually select, then execute shell commands directly in vim? [duplicate]

This question already has answers here:
Execute current line in Bash from Vim
(7 answers)
Closed 7 years ago.
When writing bash scripts in vim, it would be useful to be able to selectively run only a few lines from the script, e.g. if I have some script
#! /bin/bash
# more commands
mkdir /tmp/test_dir
echo "some output to STDOUT"
touch /tmp/test_dir/new_file
# more commands
say I just want to execute this part of the script
mkdir /tmp/test_dir
echo "some output to STDOUT"
touch /tmp/test_dir/new_file
how can I highlight and execute it in vim?
I have tried
v, select target text, :
which gives a prompt with :'<, '>
then tried :! w
but its not working properly.
I dont need to write the STDOUT of the shell commands to a file or anything, but I would like to be able to at least see the output.
From the answer of pacholik, you can extrapolate:
in visual mode, hit : and write w !bash
so that your whole command is :'<,'>w !bash
You will get the output as a result (but it won't change the file
If you remove the w , it will instead replace the line by the output of the buffer.
I have for example mapped r to "run command" in visual mode.
Like this:
vnoremap r :w !bash<cr>
I also have this in normal mode (to run the current line), with yr (you run)
nnoremap yr :.w !bash<cr>
You can use Quickrun plugin : http://vimawesome.com/plugin/quickrun-vim
It can run many languages on the fly (bash, python, c++...), and you can run only a selected area.
Everything you need is :
Set the filetype of your file (normally it's automatically detected, unless you created a new file, in this case just do for example : :set ft=sh for bash)
Select a part of your file with V.
Run :'<,'>QuickRun
The output opens in a new window.
You can tweak the plugin on many points, see the help.

GVIM: How to pass in multiple arguments via a file under windows

Is anyone aware of a command line option or a way to pass in a file to gvim which will use the contents of that file as a list of arguments?
Achieving this without having to populate argv with a list of files.
The problem is that vim is a unix tool which by default assumes that a list of files would be piped in, if there are say 1000 files that need to be opened, however in the windows world there is a limit to how many arguments you can have on the command line. The way to do this on a windows command line is to have a file which contains all the arguments you wish to pass onto the program. I am wondering if gvim provides such an option.
Note: This is to invoke gvim in a windows compatible way i.e. avoid using extremely long argument lists
A simple solution:
list your files in files,
file1
file2
file3
open Vim with the command below,
$ vim -c next `cat files`

How to execute command inside vim?

I want to run shell script inside Vim editor.
I heard it is possible but do not know.
Command:./shell.sh inside vim.
There are multiple ways to do it. A primary question is "Do you want the output from the script in the file?"
If you want the output in the file:
:r!./shell.sh
If you don't want the output in the file:
:!./shell.sh
If you have the line ./shell.sh in the file, you can include the output in the file with:
!!sh
If you've done it before, you have more options.
If you save the command in a named buffer you have still more options.
If you want the script to have a portion of the file (edit buffer) as its standard input, you have an enormous number of options you can use in conjunction with either of these mechanisms.
Prefix the command with a !. For example, open Vim and write:
:!ls
This will execute the shell ls command.
Note that you'll have to be in the correct directory within Vim for this to work.

Resources