less icon indicating copy to clipboard operation
less copied to clipboard

Make it possible to leave "follow forever" mode without pressing Ctrl+C

Open Hubro opened this issue 4 years ago • 25 comments

When piping the output of a long running process to less, the follow forever (F) command is very useful for keeping the freshest content on the screen, but unfortunately there doesn't seem to be a way to leave follow mode without also stopping the process producing output (CtrlC).

Would it be possible to add a keyboard shortcut for stopping follow forever mode without affecting other processes in the pipe?

(I suspect this is impossible, or at least extremely difficult, but it doesn't hurt to ask)

Hubro avatar Feb 25 '20 18:02 Hubro

This is partially related to #6.

aleclearmind avatar Mar 15 '20 14:03 aleclearmind

I made an attempt at this using select() but did not come up with a working solution. I abandoned the effort for now but saved my results in the alt-intr-char branch for future reference.

gwsw avatar Mar 30 '20 01:03 gwsw

Related to #62.

gwsw avatar Nov 19 '20 00:11 gwsw

I made an attempt at this using select() but did not come up with a working solution. I abandoned the effort for now but saved my results in the alt-intr-char branch for future reference.

I tried with select too but it wasn't effective. What about the pool solution I proposed #6? Back in May 2019 you seemed to be reluctant to adopt select/poll, but if you're OK with select, why not pool' less is a tool people use interactively, if there's a slightly different behavior across operating systems people will go "oh well...". It's not like we're breaking the portability of any script/piece of code out there.

aleclearmind avatar Nov 19 '20 07:11 aleclearmind

Well, poll and select perform very similar functionality, and it's not immediately clear to me that it would be any easier to implement using poll vs. select. In the Linux kernel, at least, both poll and select use the same mechanism (they both call f_op->poll on the file). In what way do you think that using poll would be better than select?

gwsw avatar Nov 19 '20 17:11 gwsw

It has been a while, but the issue with select for me was that, when a pipe is closed, it is notified among file descriptors that can be read, readfds, while I'd expect it to appear among exceptfds. On the other hand, with poll, I was able to detect quite reliably when a pipe was being closed using POLLERR and POLLHUP.

man 2 poll is pretty explicit:

POLLERR Error condition (only returned in revents; ignored in events). This bit is also set for a file descriptor referring to the write end of a pipe when the read end has been closed. POLLHUP Hang up (only returned in revents; ignored in events). Note that when reading from a channel such as a pipe or a stream socket, this event merely indicates that the peer closed its end of the channel. Subsequent reads from the channel will return 0 (end of file) only after all outstanding data in the channel has been consumed.

Or maybe I'm missing something and you tried something different with select.

Maybe we can move this discussion on #6.

aleclearmind avatar Nov 19 '20 18:11 aleclearmind

Related unix.stackexchange.com question Is there any way to exit “less” follow mode without stopping other processes in pipe? with answers giving workarounds using shell features to supress ctrl+c propagation across processes involved in a pipe.

piotr-dobrogost avatar Nov 21 '20 17:11 piotr-dobrogost

I do not see what is difficult there. At least in Bash shell you could put the reading process in background and wait for keypress like this:

cat /dev/urandom | tr -cd 'a-zA-Z0-9' & read -rn1; kill $!

You could pipe some appending input for less like this

{ cat /dev/urandom | tr -cd 'a-zA-Z0-9' & } | less +F

Though I do not understand why it writes only a screen of text without user interaction; Ctrl + C, F helps.

jarnos avatar Nov 23 '20 07:11 jarnos

Though I do not understand why it writes only a screen of text without user interaction; Ctrl + C, F helps.

Yes, that's strange. @gwsw Any thoughts on the possible reason for this behaviour?

piotr-dobrogost avatar Nov 24 '20 09:11 piotr-dobrogost

In the second example you are piping a string of alphanumerics with no newlines into less. In other words, the file is one infinitely long line. Less doesn't handle a case like that well; it expects lines to be separated by newlines.

gwsw avatar Nov 24 '20 15:11 gwsw

As of 5bf862fde4a33ce4a602f369a0c3cb430c883a12, you should be able to exit F mode with control-X, in environments where poll() is supported.

gwsw avatar Dec 01 '20 02:12 gwsw

Nice!

So Ctrl + X interrupts following, right? This is helpful under certain circumstances, but I've to say that it doesn't help me with the most annoying issue I have: when you're at the end of the stream and you just want to be able to press arrow up and scroll up. Before, I had to press Ctrl + C to kill an (already dead) process, now I can press Ctrl + X, but it's not a real improvement in usability.

Since we use poll, adding this extra feature would be great.

aleclearmind avatar Dec 01 '20 12:12 aleclearmind

The original request in this issue was to provide a way to exit F mode without terminating other processes in a pipe, which the ^X achieves. It sounds like you're asking for something different, or further, which is to avoid the need to enter a special interrupt character at all, but just allow any keypress to interrupt the read, and then get interpreted as a command. Am I understanding correctly? So your idea would just save one keypress in this situation?

gwsw avatar Dec 01 '20 17:12 gwsw

Yes, but my idea would be was to do this only once we can detect that the input pipe is closed. But, yes, I guess that interrupting follow mode on any key press, no matter the status of the input pipe, would work too, despite being a bit more of a radical approach.

aleclearmind avatar Dec 01 '20 19:12 aleclearmind

Ok, I understand what you are saying and I agree. I will continue discussion in #6.

gwsw avatar Dec 02 '20 00:12 gwsw

Does this help to solve #62? Why not use q as shortcut?

jarnos avatar Dec 03 '20 08:12 jarnos

The technique used to solve #49 might be usable to solve #62, but the current implementation does not solve #62, so I am reopening that issue.

I prefer to use ^X rather than q to exit follow mode because of the potential for accidentally quitting less. If you're in follow mode and you press q to exit, and the screen does not refresh immediately because of system load or network lag, you might hit q again, and then find that you've unexpectedly exited less when both q's are processed.

gwsw avatar Dec 03 '20 15:12 gwsw

What happens with the stdout from the pipe once F mode is left? Is it buffered? And can one resume F mode (and then get the output that happened in the meantime)?

calestyo avatar Jan 06 '21 18:01 calestyo

@calestyo It is buffered and you get output that happened in the meantime :)

simnalamburt avatar Jan 09 '21 11:01 simnalamburt

@calestyo It's buffered until it's not due to the limited size of pipe's buffer – see How big is the pipe buffer?

piotr-dobrogost avatar Feb 14 '22 13:02 piotr-dobrogost

@gwsw Sorry for bothering you, I was just wondering which release this feature (^X) was released in? :slightly_smiling_face: I'm on Arch Linux with less 590, and I noticed that ^X doesn't seem to work.

Hubro avatar Jul 26 '22 06:07 Hubro

According to https://github.com/gwsw/less/blob/22e4af5cccbfab633000c7d610f868a868ad6e1a/NEWS#L114 this should have been released in 581 - maybe you don't get HAVE_POLL set in configure in your environment?

tbussmann avatar Jul 26 '22 15:07 tbussmann

Yes, ^X should be supported in less-590, but it requires the OS to support poll(). If you built less yourself, you can check the definition of HAVE_POLL in defines.h to see whether less thinks the OS has poll(). If HAVE_POLL is set, another more disturbing possibility is that the OS supports poll() but it doesn't work correctly. This is the case in MacOS. I don't know much about Arch Linux so I don't know what support there is for poll().

gwsw avatar Jul 26 '22 17:07 gwsw

I tried building from the latest master branch.

defines.h includes:

/* Define to 1 if you have the `poll' function. */
#define HAVE_POLL 1

Pressing Ctrl+X still has no effect. I tried this in both Alacritty and xfce4-terminal, to rule out any terminal emulator keybindings eating the Ctrl+X.

I'm testing with this command:

$ tail -c +0 -f defines.h | ./less

When I press Ctrl+X, nothing happens. Does this work for you?

Hubro avatar Jul 26 '22 20:07 Hubro

You are correct that this does not work. Less checks for a ctrl-X keypress just before each read() from the input file. While less is stuck in a blocked read, it cannot see any keys from the terminal. So a case like this would work

i=1; while :; do echo $i; sleep .1; let i=$i+1; done | less

But if the pipe is producing no data ctrl-X won't work.

I think fixing this would require using poll() on the input pipe before each read to confirm that there is data ready to be read. I've done a little work on this but it's not trivial (in particular I haven't found a good way to detect EOF if I'm avoiding calling read() when there is no data available). Meanwhile I am reopening this issue.

gwsw avatar Aug 16 '22 18:08 gwsw