1

So let's say I have something like this in a shell script

command1 | command2

Now how can I make it so that command2 terminates when command1 exits?

Edit:

That usually happens by default when command2 tries to read from standard input and notices that it returns 0. Do you have some concrete example where it doesn't do that?

Here you go: echo test | xmobar

And I guess this could be used as a substitute for xmobar as well

#!/bin/sh
while :; do :; done
8
  • That will happen automatically. When command1 exits, the pipe closes and this tells command 2 it's not getting any more input.
    – Hack Saw
    Jul 8, 2020 at 0:29
  • That usually happens by default when command2 tries to read from standard input and notices that it returns 0. Do you have some concrete example where it doesn't do that? Jul 8, 2020 at 0:29
  • It doesn't make any sense to pipe input into a process that doesn't read from standard input (e.g., your script). Does xmobar read from standard input? Jul 8, 2020 at 2:20
  • 1
    @HackSaw That's not strictly true. Closing the pipe in cmd1 does not tell cmd 2 anything at all. Nothing happens until cmd2 reads the pipe, empties it, and gets an EOF. Even then, it does not have to exit if it has work still to do. Similarly, if cmd2 exits first, cmd1 does not find out about it until it writes to the pipe, at which point it receives SIGPIPE. If it traps that, it too can carry on with other work. Jul 8, 2020 at 7:54
  • 1
    @HackSaw Even more pedantically, the pipe has a pair of descriptors, and the write descriptor is released when cmd1 exits. The pipe remains open because there is an active read descriptor, and it stays open until cmd2 explicitly closes it, or cmd2 terminates. Even the EOF does not close the pipe. Jul 8, 2020 at 14:14

1 Answer 1

2

This comment explains how programs terminate (or not) when the other end of the pipe gets closed:

Closing the pipe in command1 does not tell command2 anything at all. Nothing happens until command2 reads the pipe, empties it, and gets an EOF. Even then, it does not have to exit if it has work still to do. Similarly, if command2 exits first, command1 does not find out about it until it writes to the pipe, at which point it receives SIGPIPE. If it traps that, it too can carry on with other work.

So this is what can happen automatically. Now your question:

command1 | command2

How can I make it so that command2 terminates when command1 exits [even if command2 doesn't notice or doesn't want to terminate]?

If your shell runs the pipeline in a separate process group, try to kill the entire group. E.g. Bash does this when job control is enabled (set -m). It is enabled by default for interactive shells (on systems that support it), but not in scripts.

Example code:

#!/bin/bash
set -m
( command1; kill -s INT 0 ) | command2

The entire pipeline is run in a separate process group with PGID (process group ID) equal to the PID of the very first part, which in this case is a subshell. kill 0 sends a signal to its own process group. This way we signal command2 just after command1 terminates.

Notes:

  • In Bash kill is a builtin, so kill in the example is not a separate process with PID and PGID assigned. Therefore the phrase "its own process group" above is not strict, it should be "the process group of the subshell". It's still the exact process group we want to use. The kill builtin or /bin/kill can be used, any will work.
  • command2 may change its own process group. If it does, the solution will not work.
  • For almost all signals command2 may ignore the signal or handle it in virtually any way. Choose a signal that fits your needs.
  • Calling kill just after command1 terminates may be too soon. It may cause command2 to exit before it reads and handles all the data generated by command1; and you may want it to handle all the data. Depending on how command2 behaves, it may be useful to delay kill (e.g. sleep 2; kill …). Note that (general purpose) Linux is not a real-time operating system, so any fixed delay may turn out to be too short if the circumstances are bad enough.

You must log in to answer this question.

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