You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
In new version, for some input, keys need Enter #834
On Arch Linux, less v710, but not v704, with input from some programs (e.g. js-beautify -h | less), needs to press Enter after hotkeys (e.g. qEnter to quit).
Are you trying to quit less before js-beautify has produced any output? This is a deliberate change to fix issue #807. We now avoid putting the terminal into raw mode until the piping program has produced at least one byte of output. This allows the piping program to use the terminal in normal, not raw, mode.
js-beautify -h | less shows all its output before I press q, which must be followed by Enter, for less to quit. (You may try it yourself, by installing https://github.com/beautifier/js-beautify.) This is unlike, says, ls --help | less (no need for Enter). So I'm not sure if it should be fixed in js-beautify or less, but less v704 works fine, so less should have an option to use the new/old behavior, to not break things.
js-beautify -h > beautify.out; cat beautify.out | less works fine (no need for Enter).
That's interesting. So this appears to be specifically related to the fact that js-beautify -h is piping this output to less, and not related to the actual content or content size.
Other than the fact that with cat you don't have to press Enter after q, are there some visible differences in the content in less between the cat method and without it?
Are you sometimes piping other programs (not js-beautify) into less? if yes, do they work as expected, and the issue is only with js-beautify? or does that also happen with other programs? (if it happens also with other programs, please share an example or two)
I'll let @gwsw take it from here, but that's useful info, thanks.
I can't get js_beautify to build, but I can reproduce the problem with node. The issue seems to be that node is doing a weird thing where it reads the terminal settings at startup, then prints its output, then resets the terminal to the startup settings. It doesn't actually change the settings except for the final reset, so the whole process is pointless. It does however break less, since the sequence is:
node reads settings (normal cooked mode)
node prints output
less sees start of output and sets terminal to raw mode
node finishes printing output
node sets terminal back to cooked mode
less is now running with the terminal in cooked mode
I could fix this by resetting the terminal to raw mode before every prompt but this is very ugly. An even worse "solution" is to have less delay for a little while at startup:
node -h | ( sleep 2; less )
This fixes this particular case but obviously can't work in general because we don't know how much to delay.
Another non-solution is to redirect node's file descriptors so it doesn't have access to the terminal:
I'd think the correct solution is to report to node.js that they shouldn't touch the terminal settings if stdout (or whatever stream they print to) is not a tty.
Less can't really workaround other applications which change the terminal settings while it runs...
Yeah, it does seem to be misbehavior on node's part. There has always been a problem with node doing this, but previously it usually worked because less set the terminal setting on startup fast enough that it was usually already in raw mode by the time node queried it. But it has always been a race.
The --file-size option will cause less to read the full input at startup, if the input is a pipe. That's not really the primary purpose of this option, but it's a side effect of its intended purpose.
@agguser do you agree that this is a problem in node and not in less? As avih says, node should not be changing the terminal settings if its output is not the terminal.
When you pipe f to cat or sponge then neither of them is interactive.
This is incorrect
It is absolutely correct. Neither cat nor sponge are interactive (in the example cases above).
Creating a pipeline of two interactive applications just can't work reasonably.
It can work if ...
Yes, there are conditions to make it work, which is why generally it doesn't work. You can't pipe less into vim or vice versa and have any reasonable expectation about it. The best case scenario here is that one of them decides to become non-interactive because its stdout is not a tty.
Piping ssh or svn when it's is interactive and asks for password (and disable terminal echo while at it), into less, is a recipe for trouble, and has been an endless source of pain for less over the years. People want it to work, and less might even jump through some hoops to try and make it work if the stars align, but that doesn't mean this expectation is valid.
Unless the user understands the "rules" absolutely and can control the applications to fit these rules, two or more interactive applications in a single pipeline is simply a recipe for trouble. It's really as simple as that.
When you pipe f to cat or sponge then neither of them is interactive.
This is incorrect
It is absolutely correct. Neither cat nor sponge are interactive (in the example cases above).
But f is interactive (you said "neither of them").
Piping ssh or svn when it's is interactive and asks for password (and disable terminal echo while at it), into less, is a recipe for trouble, and has been an endless source of pain for less over the years. People want it to work, and less might even jump through some hoops to try and make it work if the stars align, but that doesn't mean this expectation is valid.
What matters is that this is very useful, and it is reasonable to ask the pager not to read from the terminal while it hasn't received any data yet.
But f is interactive (you said "neither of them").
I was referring to cat and sponge.
The point is that's not a pipeline of two interactive applications. In both the examples above, neither cat nor sponge are interactive, which is why it's not the same as f | less which is a pipeline of two interactive applications.
What matters is that this is very useful, and it is reasonable to ask the pager not to read from the terminal while it hasn't received any data yet.
It already does that, as part of the hoops it tries to jump through in order to make it work.
The problem here is that this user doesn't know the "rules" to make two interactive applications in a pipeline sort-of work, and f is used interactively even after it started its output into "less".
As I said, two or more interactive applications in a pipeline require the starts to align, and by default they're typically not aligned, which is why it's a recipe for trouble.
The problem here is that this user doesn't know the "rules" to make two interactive applications in a pipeline sort-of work, and f is used interactively even after it started its output into "less".
Note, however, that the use of an intermediate sponge (last example: f | sponge | less) is expected to work for the given f (but not for a general f, e.g. which would close or redirect stdout before reading from the terminal, in which case both f and less would read from the terminal at the same time).
Note, however, that the use of an intermediate sponge (last example: f | sponge | less) is expected to work
Sure. That's one reason (of several) for utilities like sponge to exist.
But OP didn't ask for a solution. They were stating that there's a difference between f|cat or f|sponge to f|less, and this difference exists because it's generally inadvisable to combine two interactive applications in a single pipeline.
It doesn't mean "never do it", and if they know what they're doing and how to control it - for instance using sponge, then it can work sometimes. But it's still very inadvisable to try such things.
It's an ugly problem. Less does avoid using the terminal until the first byte of data has been received, but this causes other issues. For example, if the piping program takes a long time to produce the first byte, less will display the "waiting for data" message. At that point, the user can't interrupt the wait by just typing ctrl-X as usual, because the terminal is not yet in raw mode. They would have to type ctrl-X followed by RETURN, which is unexpected and nonintuitive. I wish there were some well defined protocol for simultaneously running programs to gain access to the terminal.
When there is no output yet, I don't see any real use for Ctrl-X, except for interrupting the program and quitting less, in which case the user can already do Ctrl-C. If some users really need Ctrl-X in this case, the behavior could be chosen with a new option.
To share terminal input between the program and less, I think that the solution would be to make less run the program and do something like what GNU Screen or tmux does (in a simpler way); this implies creating a pseudo-tty for the program. There might also be drawbacks I'm not aware of.
Yes, ctrl-C is probably sufficient in this particular case, but if the user is accustomed to using ctrl-X, they may type it and then be puzzled as to why it just echoes ^X and does nothing. It's just unexpected and inconsistent behavior.
Also, ^C kills the piping process while ^X does not. Perhaps the user wants to run a shell command via the ! command while continuing to wait for the piped data, or something like that.
I don't dispute that the shell command example is uncommon. But I don't think it's uncommon to type ctrl-X while waiting for the initial data. I've done it myself and been momentarily puzzled as to why it didn't interrupt the read until I remembered the implementation. I don't think that an average user is going to understand why ctrl-X works sometimes and not others.
This is something that is rather easy to learn, much less an issue than wondering what happens when two concurrent programs read terminal input at the same time.
I guess my point is that it would be BETTER if ctrl-X worked consistently, and it's unfortunate that it's impossible to make it do so because of the competing requirement to support initially-interactive programs.
Seems that a good way to view the output of an interactive app is to use tee to capture the output first, e.g.
$ f() { echo 1;read x;echo 2 $x 3; }
$ t=$(mktemp); f | tee $t; less $t; rm $t
1
0 # this input line is not shown in `less`
2 0 3
$ tless() (t=$(mktemp)&&trap'rm -f $t' EXIT && tee $t&& less $t) # define function for reuse
$ f | tless
1
0
2 0 3
# But it shows output 2 times: to stdout and to less
$ f | tless | cat
1
0
2 0 3
1
2 0 3
Activity
gwsw commentedon Sep 30, 2026
Are you trying to quit less before js-beautify has produced any output? This is a deliberate change to fix issue #807. We now avoid putting the terminal into raw mode until the piping program has produced at least one byte of output. This allows the piping program to use the terminal in normal, not raw, mode.
agguser commentedon Oct 1, 2026
js-beautify -h | lessshows all its output before I press q, which must be followed by Enter, forlessto quit. (You may try it yourself, by installing https://github.com/beautifier/js-beautify.) This is unlike, says,ls --help | less(no need for Enter). So I'm not sure if it should be fixed injs-beautifyorless, butlessv704 works fine, solessshould have an option to use the new/old behavior, to not break things.avih commentedon Oct 1, 2026
If you save this output to a file, like so:
js-beautify -h > beautify.out, does the issue also happpen if you do this?cat beautify.out | lessIf yes, does it also happen when you do this?
cat becautify.out | LESSNOCONFIG=1 lessCould you please attach this
beautify.outfile here? (zip it if you have to)agguser commentedon Oct 1, 2026
js-beautify -h > beautify.out; cat beautify.out | lessworks fine (no need for Enter).beautify.out.txt
avih commentedon Oct 1, 2026
That's interesting. So this appears to be specifically related to the fact that
js-beautify -his piping this output to less, and not related to the actual content or content size.Other than the fact that with
catyou don't have to press Enter afterq, are there some visible differences in the content in less between the cat method and without it?Are you sometimes piping other programs (not js-beautify) into less? if yes, do they work as expected, and the issue is only with
js-beautify? or does that also happen with other programs? (if it happens also with other programs, please share an example or two)I'll let @gwsw take it from here, but that's useful info, thanks.
agguser commentedon Oct 1, 2026
No differences between both outputs.
It seems to be
Node.jsspecific (js-beautifyuses it); these commands have this issue:node -h | less,node -e 'console.log(123)' | less.No such issue if non-node, e.g.:
deno eval 'console.log(123)' | lessavih commentedon Oct 1, 2026
Thanks. Good find.
One last question if you don't mind, before I let @gwsw take over, what happens with this?
node -h | LESSNOCONFIG=1 lessagguser commentedon Oct 1, 2026
node -h | LESSNOCONFIG=1 lessneeds Enter too.avih commentedon Oct 1, 2026
Thanks.
gwsw commentedon Oct 1, 2026
I can't get js_beautify to build, but I can reproduce the problem with node. The issue seems to be that node is doing a weird thing where it reads the terminal settings at startup, then prints its output, then resets the terminal to the startup settings. It doesn't actually change the settings except for the final reset, so the whole process is pointless. It does however break less, since the sequence is:
I could fix this by resetting the terminal to raw mode before every prompt but this is very ugly. An even worse "solution" is to have less delay for a little while at startup:
This fixes this particular case but obviously can't work in general because we don't know how much to delay.
Another non-solution is to redirect node's file descriptors so it doesn't have access to the terminal:
I'm trying to come up with a better solution.
avih commentedon Oct 1, 2026
I'd think the correct solution is to report to node.js that they shouldn't touch the terminal settings if stdout (or whatever stream they print to) is not a tty.
Less can't really workaround other applications which change the terminal settings while it runs...
gwsw commentedon Oct 1, 2026
Yeah, it does seem to be misbehavior on node's part. There has always been a problem with node doing this, but previously it usually worked because less set the terminal setting on startup fast enough that it was usually already in raw mode by the time node queried it. But it has always been a race.
avih commentedon Oct 1, 2026
@agguser do you think you'll be able to report it to node.js, and add a link to this issue?
agguser commentedon Oct 1, 2026
Another workaround is to fully read
node's output first, e.g.node -h | sponge | less. (Or iflesshas an option to fully read its input first.)gwsw commentedon Oct 4, 2026
The
--file-sizeoption will cause less to read the full input at startup, if the input is a pipe. That's not really the primary purpose of this option, but it's a side effect of its intended purpose.@agguser do you agree that this is a problem in node and not in less? As avih says, node should not be changing the terminal settings if its output is not the terminal.
15 remaining items
avih commentedon Oct 7, 2026
It is absolutely correct. Neither
catnorspongeare interactive (in the example cases above).Yes, there are conditions to make it work, which is why generally it doesn't work. You can't pipe less into vim or vice versa and have any reasonable expectation about it. The best case scenario here is that one of them decides to become non-interactive because its stdout is not a tty.
Piping ssh or svn when it's is interactive and asks for password (and disable terminal echo while at it), into less, is a recipe for trouble, and has been an endless source of pain for less over the years. People want it to work, and less might even jump through some hoops to try and make it work if the stars align, but that doesn't mean this expectation is valid.
Unless the user understands the "rules" absolutely and can control the applications to fit these rules, two or more interactive applications in a single pipeline is simply a recipe for trouble. It's really as simple as that.
vinc17fr commentedon Oct 7, 2026
But
fis interactive (you said "neither of them").What matters is that this is very useful, and it is reasonable to ask the pager not to read from the terminal while it hasn't received any data yet.
avih commentedon Oct 7, 2026
I was referring to cat and sponge.
The point is that's not a pipeline of two interactive applications. In both the examples above, neither cat nor sponge are interactive, which is why it's not the same as
f | lesswhich is a pipeline of two interactive applications.It already does that, as part of the hoops it tries to jump through in order to make it work.
The problem here is that this user doesn't know the "rules" to make two interactive applications in a pipeline sort-of work, and
fis used interactively even after it started its output into "less".As I said, two or more interactive applications in a pipeline require the starts to align, and by default they're typically not aligned, which is why it's a recipe for trouble.
vinc17fr commentedon Oct 7, 2026
Note, however, that the use of an intermediate
sponge(last example:f | sponge | less) is expected to work for the givenf(but not for a generalf, e.g. which would close or redirect stdout before reading from the terminal, in which case bothfandlesswould read from the terminal at the same time).avih commentedon Oct 7, 2026
Sure. That's one reason (of several) for utilities like sponge to exist.
But OP didn't ask for a solution. They were stating that there's a difference between
f|catorf|spongetof|less, and this difference exists because it's generally inadvisable to combine two interactive applications in a single pipeline.It doesn't mean "never do it", and if they know what they're doing and how to control it - for instance using sponge, then it can work sometimes. But it's still very inadvisable to try such things.
gwsw commentedon Oct 7, 2026
It's an ugly problem. Less does avoid using the terminal until the first byte of data has been received, but this causes other issues. For example, if the piping program takes a long time to produce the first byte, less will display the "waiting for data" message. At that point, the user can't interrupt the wait by just typing ctrl-X as usual, because the terminal is not yet in raw mode. They would have to type ctrl-X followed by RETURN, which is unexpected and nonintuitive. I wish there were some well defined protocol for simultaneously running programs to gain access to the terminal.
vinc17fr commentedon Oct 7, 2026
When there is no output yet, I don't see any real use for Ctrl-X, except for interrupting the program and quitting
less, in which case the user can already do Ctrl-C. If some users really need Ctrl-X in this case, the behavior could be chosen with a new option.To share terminal input between the program and
less, I think that the solution would be to makelessrun the program and do something like what GNU Screen or tmux does (in a simpler way); this implies creating a pseudo-tty for the program. There might also be drawbacks I'm not aware of.gwsw commentedon Oct 7, 2026
Yes, ctrl-C is probably sufficient in this particular case, but if the user is accustomed to using ctrl-X, they may type it and then be puzzled as to why it just echoes
^Xand does nothing. It's just unexpected and inconsistent behavior.Also, ^C kills the piping process while ^X does not. Perhaps the user wants to run a shell command via the
!command while continuing to wait for the piped data, or something like that.vinc17fr commentedon Oct 7, 2026
I'd say that this case is rather uncommon.
gwsw commentedon Oct 7, 2026
I don't dispute that the shell command example is uncommon. But I don't think it's uncommon to type ctrl-X while waiting for the initial data. I've done it myself and been momentarily puzzled as to why it didn't interrupt the read until I remembered the implementation. I don't think that an average user is going to understand why ctrl-X works sometimes and not others.
vinc17fr commentedon Oct 7, 2026
This is something that is rather easy to learn, much less an issue than wondering what happens when two concurrent programs read terminal input at the same time.
gwsw commentedon Oct 7, 2026
I guess my point is that it would be BETTER if ctrl-X worked consistently, and it's unfortunate that it's impossible to make it do so because of the competing requirement to support initially-interactive programs.
agguser commentedon Oct 8, 2026
Seems that a good way to view the output of an interactive app is to use
teeto capture the output first, e.g.Alternatively,
scriptmay be used, e.g.gwsw commentedon Oct 12, 2026
As far as I can tell, this is not a less issue, and the issue related to
nodeis currently being discussed at nodejs/node#66440.