6
gunzip < 100terabytes.txt.gz | less

less(1) reads as many lines as necessary to fill your screen and then stops to read(2). As a result, gunzip(1) is blocked on write(2) as the pipe becomes full.

As you scroll down, less(1) issues read(2) again and again, and as the pipe is consumed, gunzip(1) is allowed to issue write(2). You have full flexibility in going back and forth here (assuming gunzip < 100terabytes.txt.gz doesn't complete.)

Everything is fine so far.

You can start a text search in less(1) with /. But when the search string is not found in 100terabytes.txt, less(1) essentially becomes unresponsive. You can terminate the search with Ctrl-C. But it seems to shut down the pipe between gunzip(1) and less(1). I don't like this. I want to manually scroll down to consume more lines from gunzip(1) after I give up a text search. It this possible?

I'm not asking for advice like gunzip < 100terabytes.txt.gz | grep pattern | less

Update

You can experiment with od -v /dev/zero | less

2 Answers 2

4

When you press Ctrl+C, that's the whole shell job (process group) that receives the SIGINT, less intercepts it to abort the search, but gunzip will terminate. To avoid that, you could do:

(trap '' INT; gunzip < file.gz) | less

so that gunzip ignores the SIGINT, but note that you won't be able to interrupt gunzip anymore after that.

For gunzip, that's probably fine, as all you need to do is quit less after which gunzip will die of a SIGPIPE the next time it writes something, but for applications that just hang without outputting something, that would be more of a problem (you'd still be able to use Ctrl+Z for SIGTSTP or Ctrl+\ for SIGQUIT though).

Also note that some commands like pv or ping install their own SIGINT handler which would revert our trap '' INT.

You can create a function to save typing like:

iless() {
  (trap '' INT; "$@") | less
}

iless gunzip < file.gz

Or:

noint() (trap '' INT; "$@")

noint gunzip < file.gz | less

But note that for:

gunzip < file.gz | grep foo | less

you'd need to write it:

noint gunzip < file.gz | notint grep foo | less

or:

noint eval 'gunzip < file.gz | grep foo' | less

or:

iless eval 'gunzip < file.gz | grep foo'

An alternative is to use process substitution:

less -f <(gunzip < file.gz | grep foo)

or (though not in zsh):

less < <(gunzip < file.gz | grep foo)

In those cases, the shell doesn't include the commands inside the process substitution in a the foreground process group (except in the second case for zsh). Their process group stays the same as the shell's. That means that they don't receive a SIGINT when you press Ctrl+C.

Note that those processes won't be affected by Ctrl+Z or Ctrl+\ either.

Tested in zsh, ksh93 and bash.

1
  • 1
    Ah, that explains why... Probably I end up in hitting Ctrl-Z followed by killall -INT less and fg :-)
    – nodakai
    Mar 15, 2016 at 16:59
0

Not an exact answer but it may solve your problem. From man less:

       ESC-F  Like  F,  but  as soon as a line is found which matches the last
              search pattern, the terminal bell is rung and forward  scrolling
              stops.

This can be interrupted by ^C but I don't know if it will break the pipe, because I couldn't reproduce your problem (I think one needs a very big input file to test this). I think it will not break the pipe, because usually you can repeatedly do F then ^C on a growing file, and F still works (you still see the file growing).

The problem is: how to enter the "last search pattern" without freezing less if the pattern is not found? Maybe the ^K option to / will help:

          ^K     Highlight any text which matches the pattern on the  cur-
                 rent screen, but don't move to the first match (KEEP cur-
                 rent position).

Edit: or, more simply, use ? to "set" this "last search pattern".

1
  • I'm testing your suggestions with less 481-2.1 on Debian. ESC-F is not really useful; I can skip as many matches as possible. /^K freezes infinitely searching for the first match and Ctrl-C closes the pipe. It's just the same as plain /. You can experiment with od -v /dev/zero | less.
    – nodakai
    Mar 15, 2016 at 16:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .