I'm not sure if I understand the problem correctly, I'm basically guessing by the symptoms. I believe less in non-interactive mode works like cat, simply dumping the contents to screen. That behavior seems to be consistent with a bug I'm noticing. The following simple command works as expected, launching less upon the f1 bind in interactive mode:
fzf --bind "f1:execute(less -f {})"
Creating a simple test script (for my purposes I created a js file with 10 console.log() statements, 1 on each line), and piping that to fzf results in less window terminating as soon as invoked:
node test.js | fzf --bind "f1:execute(less -f {})"
Moreover, when quitting fzf, I see that the terminal tried to execute each one of those lines, implying that the contents of the file were simply dumped to the terminal, like cat would do. Just to test that the problem wasn't with node, I also tried catting the same file to fzf:
cat test.js | fzf --bind "f1:execute(less -f {})"
The file contents:
console.log('foo bar baz quuz');
console.log('foo bar baz quuz');
console.log('foo bar baz quuz');
console.log('foo bar baz quuz');
console.log('foo bar baz quuz');
console.log('foo bar baz quuz');
console.log('foo bar baz quuz');
console.log('foo bar baz quuz');
console.log('foo bar baz quuz');
console.log('foo bar baz quuz');
console.log('foo bar baz quuz');
Interactive programs read from standard input assuming it's a TTY device, so you need to redirect STDIN (and STDOUT) when it's not.
# Doesn't work
# Vim: Warning: Output is not to a terminal
# Vim: Warning: Input is not from a terminal
ls | fzf --bind "f1:execute(vim {})" > selected
ls | fzf --bind "f1:execute(vim {} < /dev/tty > /dev/tty 2>&1)" > selected
I'm a new user and I got burned by this too, thank you for the answer. Please consider adding this to the less example in the README.
Thanks @junegunn for answering this. I have a similar issue but a little bit more complex:
some command | fzf --bind "f1:execute(show {} | less)" | do something with output
I want to be able to both run a piped command inside the exectue( and pipe the output of fzf to another program. Where should I put the /dev/tty redirection in this case?
I tried:
some command | fzf --bind "f1:execute(show {} | less < /dev/tty > /dev/tty 2>&1)" | do something with output
But that produced weird behavior when I pressed f1.
You're redirecting the output of show {} as the standard input of less, but at the same time setting /dev/tty as the standard input as well, sending two conflicting messages to less. Instead of piping it, pass the file name to less directly.
ls | fzf --bind "f1:execute(less {} < /dev/tty > /dev/tty 2>&1)"
ls | fzf --bind "f1:execute(less -f <(cat {}) < /dev/tty > /dev/tty 2>&1)"
Most helpful comment
Interactive programs read from standard input assuming it's a TTY device, so you need to redirect STDIN (and STDOUT) when it's not.