1

I have this simple script (pipecommand.sh) that prints a prefix followed by its PID and PPID:

#!/usr/bin/env ksh
echo "$1: PID $$  PPID $PPID" >&2

And this script that prints its PID and PPID and makes a pipe:

#!/usr/bin/env ksh
echo "S: PID $$  PPID $PPID"
./pipecommand.sh A | ./pipecommand.sh B

When I run the script I get this output (I've simplified the PIDs):

S: PID 11  PPID 22
B: PID 33  PPID 11
A: PID 44  PPID 11

Okay, A and B are both children of the shell. Makes sense. Though I noticed that occasionally A's parent is /sbin/upstart --user (in ubuntu 16.10 at least). Weird, but okay.

But when I run the pipe in the background (i.e. ./pipecommand.sh A | ./pipecommand.sh B &) I get this output instead:

S: PID 11  PPID 22
B: PID 33  PPID 55
A: PID 44  PPID 33

Okay, so B's parent is upstart (PID 55). But A's parent is B (or randomly upstart as before)? What's going on? Is this a bug or is there some documentation somewhere explaining why this happens? This seems especially bad for programs that handle SIGCHLD in a specific way (which is how I came across this issue).

For comparison, in bash A and B are children of the shell in the first case, and children of upstart in the second. These results seem to be consistent.

ksh --version gives me: version sh (AT&T Research) 93u+ 2012-08-01.

  • 2
    ksh93 executes the last (rightmost) pipeline command in the parent shell, whereas bash executes all pipeline commands including the last command in a subshell. – fpmurphy Mar 23 '17 at 2:05
  • You can try that out by e.g. putting a variable assignment on the right-hand side. That is explicitly permitted by POSIX, so it's not a bug. – Michael Homer Mar 23 '17 at 4:32
  • @MichaelHomer I'm not sure what that has to do with my question. My question is why the left pipeline command's (there can be multiple) parent is the rightmost command when in the background. – Kevin Mar 23 '17 at 12:25
  • It execs the final command in a subshell, retaining the PID (also permitted). – Michael Homer Mar 23 '17 at 17:23
1

ksh, like the Bourne shell or yash, when non-interactive only waits for the rightmost command in the pipeline. You'll notice that:

ksh -c 'sleep 1 | true'

returns immediately there.

In your case, sometimes, the parent has already gone by the time A starts and does a getppid() to fill in the $PPID variable, and you get the pid of init or the child subreaper which has adopted the process after its father died.

It's not a bug. It's by choice. Doing it that way instead of waiting for every pipeline component has its advantages (like some improved performance in most situations) and inconvenience like unexpected behaviour like this one or if the commands on the left side of the pipeline produce results which the next command in the script make use of.

  • I think you're right. But it seems that bash does wait for all commands to exit - bash -c 'sleep 1 | true' doesn't exit immediately. Same for sh and zsh (I don't have yash on my machine to test that). So that would explain why the results with bash are different. – Kevin Aug 18 '17 at 16:03
  • But I still feel like this is a bug. How ksh handles pipes in certain cases shouldn't affect the behavior of the commands run in the pipe (excluding stdin/stdout obviously). For commands that handle SIGCHLD this is a pretty big issue. – Kevin Aug 18 '17 at 16:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.