Manually select/unselect lines before forwarding to stdout
While I like the vipe
approach, I've found an even cooler way: fzf.
cat people.txt | grep -v "^#" | grep "^.*Fruit.*Folk" | fzf -m | cat
...gives a nice list where I can search in and toggle the items with the keyboard and disappears completely when I am done.
moreutils has vipe
, which just runs your regular text editor (vi or something) as part of the pipeline. Any lines that you don't delete will get written back to stdout. (In general, though, you get the same result by redirecting to a temporary file and editing that file.)
You can also use gvim
(and vim
) for this. Insert gvim /dev/stdin
into your pipe, and write by doing :wq! /dev/stdout
when you're finished editing.
Calling gvim as the following lets you more naturally write and close with ZZ
:
gvim +'nmap ZZ :wq!<cr> | r /dev/stdin | 1d | file /dev/stdout'
You can make an alias or a function of that:
gvimp() {
gvim +'
nmap ZZ :wq!<cr>
r /dev/stdin
1d
file /dev/stdout
'
}
Here's an example where I deleted lines 4-7 and finished by doing ZZ
in normal mode:
$ seq 1 10 | gvimp | sed -r 's/^|$/=/g'
=1=
=2=
=3=
=8=
=9=
=10=
EDIT: Regular vim is a little more tricky, because it uses stdin for keyboard input and stdout for drawing the user interface. However, you can still use it by adding an extra process. You redirect vim's std* to the terminal, and use the parent process' std* for the data pipe.
vimp() {
bash -c '
terminal="/dev/$(ps -o tty= $$)"
tmp_out="$(mktemp)"
trap "rm \$tmp_out" EXIT
vim <(cat) < "$terminal" &> "$terminal" +"
file $tmp_out
set noreadonly
nmap ZZ :wq!<cr>
"
cat "$tmp_out"
'
}
The temporary file is necessary because otherwise the pipe output is going to get mixed with vim's user interface and might disappear with it when vim exits.