zsh-users
 help / color / mirror / code / Atom feed
* kill the LHS command of a pipe once the RHS command terminates
@ 2019-06-28 11:04 Vincent Lefevre
  2019-06-28 11:38 ` Vincent Lefevre
                   ` (2 more replies)
  0 siblings, 3 replies; 20+ messages in thread
From: Vincent Lefevre @ 2019-06-28 11:04 UTC (permalink / raw)
  To: zsh-users

With some commands I pipe the output to a pager, and when quitting
the pager, I want the command to terminate immediately (so that it
doesn't consume more resources) and want the shell prompt back.

Here are examples with "head -n 1" instead of a pager.

* With just a pipe:

zira% { echo foo; sleep 3; echo err >&2 } | head -n 1
foo
[wait for 3 seconds]
err
zira%

The issue is that when "head -n 1" terminates (just after "echo foo"),
the left-hand-side command is still running. Thus it still consumes
resources, and I also have to wait for the shell prompt, which is bad.

* With a process substitution:

zira% head -n 1 <(echo foo; sleep 3; echo err >&2)
foo
zira%

Here I get the shell prompt back immediately, which is good. But
the left-hand-side command is still running, so that it still
consumes resources; moreover, after the 3 seconds, I get

zira% err

In practice, one may want to redirect stderr to the pager too (not
always, though); this avoids this second issue, but not the first one,
which is that the left-hand-side command still consumes resources.

Does zsh provide a feature to kill the left-hand-side command in a
clean way (without a race condition, without a temporary file...)?

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-06-28 11:04 kill the LHS command of a pipe once the RHS command terminates Vincent Lefevre
@ 2019-06-28 11:38 ` Vincent Lefevre
  2019-06-29 16:17 ` Bart Schaefer
  2019-07-29 15:23 ` Vincent Lefevre
  2 siblings, 0 replies; 20+ messages in thread
From: Vincent Lefevre @ 2019-06-28 11:38 UTC (permalink / raw)
  To: zsh-users

On 2019-06-28 13:04:30 +0200, Vincent Lefevre wrote:
> * With a process substitution:
> 
> zira% head -n 1 <(echo foo; sleep 3; echo err >&2)
> foo
> zira%
> 
> Here I get the shell prompt back immediately, which is good. But
> the left-hand-side command is still running, so that it still
> consumes resources; moreover, after the 3 seconds, I get
> 
> zira% err
> 
> In practice, one may want to redirect stderr to the pager too (not
> always, though); this avoids this second issue, but not the first one,
> which is that the left-hand-side command still consumes resources.

There's another issue, but that's a bug in "less": If one uses the
less -c option (in addition to -f, needed with process substitution),
Ctrl-C no longer has any effect: one cannot interrupt the command to
go back to the shell. Example:

zira% less -fc <(echo foo; sleep 3; echo err >&2)

(tested under Debian/unstable).

And I've noticed that zsh doesn't react to Ctrl-C either in the
following case:

zira% { echo foo; sleep 3; echo err >&2 } | { head -n 1 }

while Ctrl-C terminates the pipeline as expected with

zira% { echo foo; sleep 3; echo err >&2 } | head -n 1

With

zira% { echo foo; sleep 3; echo bar; sleep 3; echo err >&2 } | { head -n 2 }

I can terminate the pipeline during the first "sleep 3" (i.e. while
"head -n 2" is still running), but not during the second "sleep 3"
(i.e. once "head -n 2" has terminated).

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-06-28 11:04 kill the LHS command of a pipe once the RHS command terminates Vincent Lefevre
  2019-06-28 11:38 ` Vincent Lefevre
@ 2019-06-29 16:17 ` Bart Schaefer
  2019-07-06 23:55   ` Vincent Lefevre
  2019-07-29 15:23 ` Vincent Lefevre
  2 siblings, 1 reply; 20+ messages in thread
From: Bart Schaefer @ 2019-06-29 16:17 UTC (permalink / raw)
  To: Zsh Users

On Fri, Jun 28, 2019 at 4:05 AM Vincent Lefevre <vincent@vinc17.net> wrote:
>
> With some commands I pipe the output to a pager, and when quitting
> the pager, I want the command to terminate immediately (so that it
> doesn't consume more resources) and want the shell prompt back.

This is going to happen in cases where the left-side command doesn't
produce enough output to fill the OS buffer used for the pipe, so it
doesn't see the end-of-file or receive the PIPE signal when the
right-side command closes the other end.  In your examples, you've
also got compound structures that are actually ignoring the state of
their standard output once the small amount of initial output has been
sent.  Neither the shell nor the OS has any way of "knowing" that you
didn't INTEND those commands to continue running.

> Does zsh provide a feature to kill the left-hand-side command in a
> clean way (without a race condition, without a temporary file...)?

That's supposed to be the EOF+SIGPIPE, but it only works for "well
behaved" commands that are actually doing something useful with their
standard output.

In addition to Stephane's suggestions, you can also get control of
your left-side by using "coproc".  This creates an actual job table
entry that you can manipulate.

zsh% coproc { echo foo; sleep 3; echo err >&2 }; head -n 1 <&p; kill %+
[1] 53747
foo
zsh%
[1]  + terminated  { echo foo; sleep 3; echo err >&2; }
zsh%

If you're going to kill the right-side with an interrupt or other
signal, you'll have to arrange to trap that so that the subsequent
kill isn't also skipped.  Also in practice you want something more
specific than "kill %+" which could kill the wrong job if you have
multiple backgrounded jobs and the coproc exits before the kill
occurs.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-06-29 16:17 ` Bart Schaefer
@ 2019-07-06 23:55   ` Vincent Lefevre
  2019-07-07  4:54     ` Bart Schaefer
  0 siblings, 1 reply; 20+ messages in thread
From: Vincent Lefevre @ 2019-07-06 23:55 UTC (permalink / raw)
  To: zsh-users

On 2019-06-29 09:17:13 -0700, Bart Schaefer wrote:
> zsh% coproc { echo foo; sleep 3; echo err >&2 }; head -n 1 <&p; kill %+
> [1] 53747
> foo
> zsh%
> [1]  + terminated  { echo foo; sleep 3; echo err >&2; }
> zsh%
> 
> If you're going to kill the right-side with an interrupt or other
> signal, you'll have to arrange to trap that so that the subsequent
> kill isn't also skipped.  Also in practice you want something more
> specific than "kill %+" which could kill the wrong job if you have
> multiple backgrounded jobs and the coproc exits before the kill
> occurs.

Something like that seems to work:

  unsetopt NOTIFY
  coproc ...
  local j="${${$(jobs %+)%%]*}#\[}"
  less -f <&p
  kill %$j

but I would like to get rid of the job status that appear at the end.

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-07-06 23:55   ` Vincent Lefevre
@ 2019-07-07  4:54     ` Bart Schaefer
  2019-07-09 10:08       ` Vincent Lefevre
  0 siblings, 1 reply; 20+ messages in thread
From: Bart Schaefer @ 2019-07-07  4:54 UTC (permalink / raw)
  To: Zsh Users

On Sat, Jul 6, 2019 at 4:56 PM Vincent Lefevre <vincent@vinc17.net> wrote:
>
> On 2019-06-29 09:17:13 -0700, Bart Schaefer wrote:
> >
> > If you're going to kill the right-side with an interrupt or other
> > signal, you'll have to arrange to trap that so that the subsequent
> > kill isn't also skipped.
>
> but I would like to get rid of the job status that appear at the end.

zsh% () {
setopt localoptions nonotify nomonitor
coproc { echo foo; exec >&-; sleep 60 }
trap "kill $!" INT EXIT
less -f <&p
}

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-07-07  4:54     ` Bart Schaefer
@ 2019-07-09 10:08       ` Vincent Lefevre
  2019-07-09 10:54         ` Vincent Lefevre
  0 siblings, 1 reply; 20+ messages in thread
From: Vincent Lefevre @ 2019-07-09 10:08 UTC (permalink / raw)
  To: zsh-users

On 2019-07-06 21:54:52 -0700, Bart Schaefer wrote:
> On Sat, Jul 6, 2019 at 4:56 PM Vincent Lefevre <vincent@vinc17.net> wrote:
> >
> > On 2019-06-29 09:17:13 -0700, Bart Schaefer wrote:
> > >
> > > If you're going to kill the right-side with an interrupt or other
> > > signal, you'll have to arrange to trap that so that the subsequent
> > > kill isn't also skipped.
> >
> > but I would like to get rid of the job status that appear at the end.
> 
> zsh% () {
> setopt localoptions nonotify nomonitor
> coproc { echo foo; exec >&-; sleep 60 }
> trap "kill $!" INT EXIT
> less -f <&p
> }

I've noticed 2 issues:

1. It can kill a wrong process of the coproc has terminated.
One needs to make sure that zsh does not wait for the coproc
until the function exits.

2. If the coproc is killed by the trap, I still get a notification.
With NOTIFY set globally, this happens during the trap, as I can
still see the notification with

  trap "kill $!; sleep 1" INT EXIT

before the 1-second delay from the sleep. A bug in localoptions,
which does not take traps into account?

If I unset NOTIFY globally, I get the notification just before
the prompt is displayed. But I don't think this is expected
either, as the child terminated when nomonitor was in effect.

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-07-09 10:08       ` Vincent Lefevre
@ 2019-07-09 10:54         ` Vincent Lefevre
  0 siblings, 0 replies; 20+ messages in thread
From: Vincent Lefevre @ 2019-07-09 10:54 UTC (permalink / raw)
  To: zsh-users

On 2019-07-09 12:08:50 +0200, Vincent Lefevre wrote:
> I've noticed 2 issues:
> 
> 1. It can kill a wrong process of the coproc has terminated.
                                 ^^ if

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-06-28 11:04 kill the LHS command of a pipe once the RHS command terminates Vincent Lefevre
  2019-06-28 11:38 ` Vincent Lefevre
  2019-06-29 16:17 ` Bart Schaefer
@ 2019-07-29 15:23 ` Vincent Lefevre
  2019-07-29 15:54   ` Bart Schaefer
  2 siblings, 1 reply; 20+ messages in thread
From: Vincent Lefevre @ 2019-07-29 15:23 UTC (permalink / raw)
  To: zsh-users

On 2019-06-28 13:04:30 +0200, Vincent Lefevre wrote:
> * With a process substitution:
> 
> zira% head -n 1 <(echo foo; sleep 3; echo err >&2)
> foo
> zira%

Another issue is that if the producer side tries to access the terminal
(e.g. ssh, for a passphrase), all the processes of this side are stopped
due to a SIGTTOU signal.

Concerning the documentation, the zshexpn(1) man page does not say
that a process in process substitution is run in background. Later
there's a mention of it being run asynchronously, but this term has
never been defined.

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-07-29 15:23 ` Vincent Lefevre
@ 2019-07-29 15:54   ` Bart Schaefer
  2019-07-29 20:48     ` Vincent Lefevre
  0 siblings, 1 reply; 20+ messages in thread
From: Bart Schaefer @ 2019-07-29 15:54 UTC (permalink / raw)
  To: Zsh Users

On Mon, Jul 29, 2019 at 8:24 AM Vincent Lefevre <vincent@vinc17.net> wrote:
>
> On 2019-06-28 13:04:30 +0200, Vincent Lefevre wrote:
> >
> > zira% head -n 1 <(echo foo; sleep 3; echo err >&2)
> > foo
>
> Another issue is that if the producer side tries to access the terminal
> (e.g. ssh, for a passphrase), all the processes of this side are stopped
> due to a SIGTTOU signal.

Even if they weren't stopped by TTOU, they'd almost certainly be
stopped by TTIN.

> Concerning the documentation, the zshexpn(1) man page does not say
> that a process in process substitution is run in background.

They have to be, otherwise either you'd need unlimited buffering or
the read of the descriptor could deadlock.  If you want it run in the
foreground, use =(...).

> Later
> there's a mention of it being run asynchronously, but this term has
> never been defined.

In the JOBS section:

       If  the  MONITOR  option  is set, an interactive shell associates a job
       with each pipeline.  It keeps a table of current jobs, printed  by  the
       jobs  command,  and  assigns them small integer numbers.  When a job is
       started asynchronously with `&', the shell prints a  line  to  standard
       error which looks like:

              [1] 1234

       indicating that the job which was started asynchronously was job number
       1 and had one (top-level) process, whose process ID was 1234.

And then in the SIGNALS section:

       Certain jobs are run asynchronously  by  the  shell  other  than  those
       explicitly put into the background; even in cases where the shell would
       usually wait for such jobs, an explicit exit command or exit due to the
       option ERR_EXIT will cause the shell to exit without waiting.  Examples
       of such asynchronous jobs are process  substitution,  see  the  section
       PROCESS  SUBSTITUTION  in  the  zshexpn(1) manual page, and the handler
       processes for multios, see the section MULTIOS in the zshmisc(1) manual
       page.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-07-29 15:54   ` Bart Schaefer
@ 2019-07-29 20:48     ` Vincent Lefevre
  2019-07-30  0:00       ` Bart Schaefer
  0 siblings, 1 reply; 20+ messages in thread
From: Vincent Lefevre @ 2019-07-29 20:48 UTC (permalink / raw)
  To: zsh-users

On 2019-07-29 08:54:19 -0700, Bart Schaefer wrote:
> On Mon, Jul 29, 2019 at 8:24 AM Vincent Lefevre <vincent@vinc17.net> wrote:
> >
> > On 2019-06-28 13:04:30 +0200, Vincent Lefevre wrote:
> > >
> > > zira% head -n 1 <(echo foo; sleep 3; echo err >&2)
> > > foo
> >
> > Another issue is that if the producer side tries to access the terminal
> > (e.g. ssh, for a passphrase), all the processes of this side are stopped
> > due to a SIGTTOU signal.
> 
> Even if they weren't stopped by TTOU, they'd almost certainly be
> stopped by TTIN.

Yes.

> > Concerning the documentation, the zshexpn(1) man page does not say
> > that a process in process substitution is run in background.
> 
> They have to be, otherwise either you'd need unlimited buffering or
> the read of the descriptor could deadlock.

I don't see how this is related to buffering. This would be the same
case as with a pipe (cmd1 | cmd2), for which both commands run in
foreground and there are no buffering issues.

> If you want it run in the foreground, use =(...).

That's not OK, because the main command is started only when
the =(...) command terminates:

zira% echo =(echo a; sleep 3; echo b)

gives its output (the temporary file) only after 3 seconds. And even
without this problem, I'm not sure that this would work with a pager,
as it would think that once a EOF has occurred, there is no more
output.

> > Later there's a mention of it being run asynchronously, but this
> > term has never been defined.
> 
> In the JOBS section:
> 
>        If the MONITOR option is set, an interactive shell associates
>        a job with each pipeline. It keeps a table of current jobs,
>        printed by the jobs command, and assigns them small integer
>        numbers. When a job is started asynchronously with `&', the
>        shell prints a line to standard error which looks like:
[...]

As described, the "asynchronously" concerns only jobs started with "&".
BTW, there is nothing in the job table:

zira% echo <(sleep 3); jobs
/proc/self/fd/11
zira%

> And then in the SIGNALS section:
> 
>        Certain jobs are run asynchronously by the shell other than
>        those explicitly put into the background;

It says that jobs explicitly put into the background are run
asynchronously, but nothing about the converse.

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-07-29 20:48     ` Vincent Lefevre
@ 2019-07-30  0:00       ` Bart Schaefer
  2019-07-30  0:17         ` Vincent Lefevre
  0 siblings, 1 reply; 20+ messages in thread
From: Bart Schaefer @ 2019-07-30  0:00 UTC (permalink / raw)
  To: Zsh Users

On Mon, Jul 29, 2019 at 1:50 PM Vincent Lefevre <vincent@vinc17.net> wrote:
>
> > > Concerning the documentation, the zshexpn(1) man page does not say
> > > that a process in process substitution is run in background.
> >
> > They have to be, otherwise either you'd need unlimited buffering or
> > the read of the descriptor could deadlock.
>
> I don't see how this is related to buffering. This would be the same
> case as with a pipe (cmd1 | cmd2), for which both commands run in
> foreground and there are no buffering issues.

You're wrong about this.  In (cmd1 | cmd2), zsh forks off cmd1 into
its own process.  It is effectively "in the background" even though
the entire pipeline is considered to be in the foreground; the only
process that is really "in the foreground" is cmd2.  If cmd2 stops
reading its stdin, cmd1 will eventually either block or get a SIGPIPE.

If both processes were in the foreground, they could get into a state
where neither can make any progress.  The same is true with <(...).

(Aside, in bash/ksh93 (cmd1 | cmd2) puts cmd2 in a forked process
instead, and keeps cmd1 in control.)

> It says that jobs explicitly put into the background are run
> asynchronously, but nothing about the converse.

I'm not sure how you get "nothing about the converse" from "Certain
jobs are run asynchronously  ... OTHER THAN THOSE explicitly put into
the background;" ("other than" refers to "certain jobs" that are
asynchronous but "(not) explicitly" backgrounded) and "Examples of
such asynchronous jobs are process  substitution" ... but in any case
this was one of those cases where the zsh manual assumed you know how
"most" shells work and therefore what these terms mean, and that it
only had to explain what it might be doing differently.  The whole
manual page used to be written this way and we are only gradually
turning it into a standalone reference.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-07-30  0:00       ` Bart Schaefer
@ 2019-07-30  0:17         ` Vincent Lefevre
  0 siblings, 0 replies; 20+ messages in thread
From: Vincent Lefevre @ 2019-07-30  0:17 UTC (permalink / raw)
  To: zsh-users

On 2019-07-29 17:00:30 -0700, Bart Schaefer wrote:
> On Mon, Jul 29, 2019 at 1:50 PM Vincent Lefevre <vincent@vinc17.net> wrote:
> >
> > > > Concerning the documentation, the zshexpn(1) man page does not say
> > > > that a process in process substitution is run in background.
> > >
> > > They have to be, otherwise either you'd need unlimited buffering or
> > > the read of the descriptor could deadlock.
> >
> > I don't see how this is related to buffering. This would be the same
> > case as with a pipe (cmd1 | cmd2), for which both commands run in
> > foreground and there are no buffering issues.
> 
> You're wrong about this.  In (cmd1 | cmd2), zsh forks off cmd1 into
> its own process.  It is effectively "in the background" even though
> the entire pipeline is considered to be in the foreground; the only
> process that is really "in the foreground" is cmd2.

No, cmd1 is definitely not run in background, as ssh (as a descendent
of cmd1) still has access to the controlling terminal: I can type the
passphrase, which was not the case with <() process substitution.

> > It says that jobs explicitly put into the background are run
> > asynchronously, but nothing about the converse.
> 
> I'm not sure how you get "nothing about the converse" from "Certain
> jobs are run asynchronously  ... OTHER THAN THOSE explicitly put into
> the background;" ("other than" refers to "certain jobs" that are
> asynchronous but "(not) explicitly" backgrounded) and "Examples of
> such asynchronous jobs are process  substitution" ... but in any case

I meant that zsh does not say whether jobs that are run asynchronously
are necessarily background processes (thus with no access to the
controlling terminal).

> this was one of those cases where the zsh manual assumed you know how
> "most" shells work and therefore what these terms mean, and that it
> only had to explain what it might be doing differently.  The whole
> manual page used to be written this way and we are only gradually
> turning it into a standalone reference.

The reference is POSIX, and POSIX does not seem to use the notion of
"run asynchronously" as in zsh. There are "asynchronous lists", but
these are exclusively commands terminated by "&".

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-07-29 23:42             ` Bart Schaefer
@ 2019-07-30  0:40               ` Vincent Lefevre
  0 siblings, 0 replies; 20+ messages in thread
From: Vincent Lefevre @ 2019-07-30  0:40 UTC (permalink / raw)
  To: zsh-users

On 2019-07-29 16:42:18 -0700, Bart Schaefer wrote:
> On Mon, Jul 29, 2019 at 4:15 PM Vincent Lefevre <vincent@vinc17.net> wrote:
> >
> > zira% (trap '' INT; svn log | { (trap - INT; less;); kill -PIPE 0; })
> 
> This is fine, but note that "kill ... 0" sends the signal to the whole
> process group, which might have unexpected side-effects.

Yes, but I think that this is what I eventually expect. For instance,
if svn has started a ssh, then the ssh should be killed (whether it is
killed by svn or by the kill command above does not seem to matter).

BTW, this can be simplified to

  (trap '' INT; svn log | { (less); kill -PIPE 0 })

because

    ( list )
        Execute list in a subshell.  Traps set by the trap builtin are
        reset to their default values while executing list.

so no need to explicitly reset the INT trap to its default value.

> Bash and ksh93 manage process groups differently than zsh; this is
> related to the way zsh runs the right-hand-side of pipelines in the
> current shell when it can.
> 
> I don't know about dash or mksh, but but they must have more zsh-like
> group management.

Bash, dash and mksh execute both sides in a subshell, but not ksh93,
which behaves like zsh, well, with a difference that may be a bug in
zsh:

  { /bin/sleep 10 ; /bin/sleep 20; } | { /bin/sleep 30 ; /bin/sleep 40; }

can be interrupted by Ctrl-C in bash, dash, mksh and ksh93, but
not in zsh. But if I remove one of the sleep, this can also be
interrupted by Ctrl-C in zsh.

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-07-29 23:15           ` Vincent Lefevre
@ 2019-07-29 23:42             ` Bart Schaefer
  2019-07-30  0:40               ` Vincent Lefevre
  0 siblings, 1 reply; 20+ messages in thread
From: Bart Schaefer @ 2019-07-29 23:42 UTC (permalink / raw)
  To: Zsh Users

On Mon, Jul 29, 2019 at 4:15 PM Vincent Lefevre <vincent@vinc17.net> wrote:
>
> zira% (trap '' INT; svn log | { (trap - INT; less;); kill -PIPE 0; })

This is fine, but note that "kill ... 0" sends the signal to the whole
process group, which might have unexpected side-effects.

Bash and ksh93 manage process groups differently than zsh; this is
related to the way zsh runs the right-hand-side of pipelines in the
current shell when it can.

I don't know about dash or mksh, but but they must have more zsh-like
group management.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-07-29 23:08         ` Vincent Lefevre
@ 2019-07-29 23:15           ` Vincent Lefevre
  2019-07-29 23:42             ` Bart Schaefer
  0 siblings, 1 reply; 20+ messages in thread
From: Vincent Lefevre @ 2019-07-29 23:15 UTC (permalink / raw)
  To: zsh-users

On 2019-07-30 01:08:17 +0200, Vincent Lefevre wrote:
> With "kill -9", this is a bit better:
> 
> zira% (trap '' INT; svn log | { (trap - INT; less;); kill -9 0; })
> zsh: killed     ( trap '' INT; svn log | { ( trap - INT; less; ); kill -9 0; }; )
> 
> But I would like to get rid of this message.

If I use the PIPE signal as suggested by Stephane, this seems to
completely solve this problem.

zira% (trap '' INT; svn log | { (trap - INT; less;); kill -PIPE 0; })

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-07-06 23:49       ` Vincent Lefevre
@ 2019-07-29 23:08         ` Vincent Lefevre
  2019-07-29 23:15           ` Vincent Lefevre
  0 siblings, 1 reply; 20+ messages in thread
From: Vincent Lefevre @ 2019-07-29 23:08 UTC (permalink / raw)
  To: zsh-users

On 2019-07-07 01:49:19 +0200, Vincent Lefevre wrote:
> Actually I suppose that Ctrl-C kills the subshell, and the pager
> as a consequence. And if I use the following,
> 
> page-and-kill() (trap '' INT; "$@" | { trap - INT; less; kill 0; })
> 
> the "kill 0" no longer kills the command. This can be seen with:
> 
> (trap '' INT; { echo foo; sleep 10; } | { trap - INT; less; kill 0; })
> 
> after typing Ctrl-C in "less", then q to quit. I've noticed that
> dash and mksh have the same issue, but neither bash, nor ksh93.

Actually a better example should be:

  (trap '' INT; sleep 10 | { trap - INT; less; echo foo; kill 0; })

If I wait for 10 seconds, then type 'q' in less, I get:

zira% (trap '' INT; sleep 10 | { trap - INT; less; echo foo; kill 0; })
foo
zsh: terminated  ( trap '' INT; sleep 10 | { trap - INT; less; echo foo; kill 0; }; )
zira% 

But if I type Ctrl-C then 'q' in less, the "sleep 10" is still
running, and I get after the 10 seconds:

zira% (trap '' INT; sleep 10 | { trap - INT; less; echo foo; kill 0; })
zira% 

i.e. no "foo" output and no "zsh: killed" line, i.e. after the Ctrl-C
in less, zsh terminated the right-hand side. I suppose that this is
due to WUE, and both bash and ksh93 do not have this issue as they
implement WCE.

It seems that putting "trap - INT; less;" in a subshell does the work.
However, I get annoying messages:

zira% (trap '' INT; svn log | { (trap - INT; less;); kill 0; })
svn: E000004: Write error: Interrupted system call
zsh: terminated  ( trap '' INT; svn log | { ( trap - INT; less; ); kill 0; }; )

With "kill -9", this is a bit better:

zira% (trap '' INT; svn log | { (trap - INT; less;); kill -9 0; })
zsh: killed     ( trap '' INT; svn log | { ( trap - INT; less; ); kill -9 0; }; )

But I would like to get rid of this message.

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-06-29 15:30     ` Stephane Chazelas
@ 2019-07-06 23:49       ` Vincent Lefevre
  2019-07-29 23:08         ` Vincent Lefevre
  0 siblings, 1 reply; 20+ messages in thread
From: Vincent Lefevre @ 2019-07-06 23:49 UTC (permalink / raw)
  To: zsh-users

On 2019-06-29 16:30:55 +0100, Stephane Chazelas wrote:
> 2019-06-29 03:24:33 +0200, Vincent Lefevre:
> [...]
> > Thus if the left-hand-side process has terminated, this tries to
> > kill a random process!
> [...]
> 
> Fair enough.
> 
> Then, how about:
> 
> page-and-kill() ("$@" | { ${PAGER:-less}; kill -s PIPE 0; })

Unfortunately, this is not usable as a Ctrl-C kills the pager.
Actually I suppose that Ctrl-C kills the subshell, and the pager
as a consequence. And if I use the following,

page-and-kill() (trap '' INT; "$@" | { trap - INT; less; kill 0; })

the "kill 0" no longer kills the command. This can be seen with:

(trap '' INT; { echo foo; sleep 10; } | { trap - INT; less; kill 0; })

after typing Ctrl-C in "less", then q to quit. I've noticed that
dash and mksh have the same issue, but neither bash, nor ksh93.

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-06-29  1:24   ` Vincent Lefevre
@ 2019-06-29 15:30     ` Stephane Chazelas
  2019-07-06 23:49       ` Vincent Lefevre
  0 siblings, 1 reply; 20+ messages in thread
From: Stephane Chazelas @ 2019-06-29 15:30 UTC (permalink / raw)
  To: zsh-users

2019-06-29 03:24:33 +0200, Vincent Lefevre:
[...]
> Thus if the left-hand-side process has terminated, this tries to
> kill a random process!
[...]

Fair enough.

Then, how about:

page-and-kill() ("$@" | { ${PAGER:-less}; kill -s PIPE 0; })

(only for use from an interactive shell where that page-and-kill
is run in its own process group).

That would also kill the extra processes that the command may
spawn.

-- 
Stephane

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
  2019-06-28 18:41 ` Stephane Chazelas
@ 2019-06-29  1:24   ` Vincent Lefevre
  2019-06-29 15:30     ` Stephane Chazelas
  0 siblings, 1 reply; 20+ messages in thread
From: Vincent Lefevre @ 2019-06-29  1:24 UTC (permalink / raw)
  To: zsh-users

On 2019-06-28 19:41:34 +0100, Stephane Chazelas wrote:
> So here with zsh, you can do:
> 
> zmodload zsh/system
> 
> page-and-kill-cmd() {
>   (echo $sysparams[pid]; "$@") | (
>     IFS= read -r pid
>     ${PAGER:-less}
>     kill -s PIPE $pid
>   )
> }

This is not OK.

zira% page-and-kill-cmd eval 'seq 100'
page-and-kill-cmd:kill:4: kill 24157 failed: no such process

Thus if the left-hand-side process has terminated, this tries to
kill a random process!

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: kill the LHS command of a pipe once the RHS command terminates
       [not found] <20190628110430.GA13790__36317.6205357135$1561719956$gmane$org@zira.vinc17.org>
@ 2019-06-28 18:41 ` Stephane Chazelas
  2019-06-29  1:24   ` Vincent Lefevre
  0 siblings, 1 reply; 20+ messages in thread
From: Stephane Chazelas @ 2019-06-28 18:41 UTC (permalink / raw)
  To: zsh-users

2019-06-28 13:04:30 +0200, Vincent Lefevre:
> With some commands I pipe the output to a pager, and when quitting
> the pager, I want the command to terminate immediately (so that it
> doesn't consume more resources) and want the shell prompt back.
[...]

See also:

https://unix.stackexchange.com/questions/404272/how-to-exit-early-on-pipe-close?noredirect=1&lq=1
https://unix.stackexchange.com/questions/366797/grep-slow-to-exit-after-finding-match/366806#366806
https://unix.stackexchange.com/questions/416150/make-tail-f-exit-on-a-broken-pipe?noredirect=1&lq=1

So here with zsh, you can do:

zmodload zsh/system

page-and-kill-cmd() {
  (echo $sysparams[pid]; "$@") | (
    IFS= read -r pid
    ${PAGER:-less}
    kill -s PIPE $pid
  )
}

And run:

page-and-kill-cmd cmd args

For a complex command, use eval:

page-and-kill-cmd eval 'seq 100; sleep 10; seq 10'

That won't work in all the cases as we're only killing one pid.

-- 
Stephane

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2019-07-30  0:41 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-28 11:04 kill the LHS command of a pipe once the RHS command terminates Vincent Lefevre
2019-06-28 11:38 ` Vincent Lefevre
2019-06-29 16:17 ` Bart Schaefer
2019-07-06 23:55   ` Vincent Lefevre
2019-07-07  4:54     ` Bart Schaefer
2019-07-09 10:08       ` Vincent Lefevre
2019-07-09 10:54         ` Vincent Lefevre
2019-07-29 15:23 ` Vincent Lefevre
2019-07-29 15:54   ` Bart Schaefer
2019-07-29 20:48     ` Vincent Lefevre
2019-07-30  0:00       ` Bart Schaefer
2019-07-30  0:17         ` Vincent Lefevre
     [not found] <20190628110430.GA13790__36317.6205357135$1561719956$gmane$org@zira.vinc17.org>
2019-06-28 18:41 ` Stephane Chazelas
2019-06-29  1:24   ` Vincent Lefevre
2019-06-29 15:30     ` Stephane Chazelas
2019-07-06 23:49       ` Vincent Lefevre
2019-07-29 23:08         ` Vincent Lefevre
2019-07-29 23:15           ` Vincent Lefevre
2019-07-29 23:42             ` Bart Schaefer
2019-07-30  0:40               ` Vincent Lefevre

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).