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.