Home > Enterprise >  Is it possible to implement command substitution via pipeline?
Is it possible to implement command substitution via pipeline?

Time:11-20

For example, we have $(ls):

int pfd[2];
pipe(pfd);
switch (fork()) {
    case 0:
    close(pfd[0]);
    dup2(pfd[1], 1 /*stdout*/);
    exec('ls', 'ls');
    case -1;
    // Report the error...
    break;
    default;
    break;
}
wait(nullptr); // Wait until the process is done (it is better to use the waitpid() version)
// And now we can read from pfd[0]

This code is very conceptual, but am I right? Would it be possible to extract the data from the write end of the pipe after the child process finishes? Than all that's left is just to substitute on substring ($(ls)) to another (the result of the ls itself). Please, correct me if I'm wrong.

And even if pfd[0] is a valid file descriptor, which points to a buffer with the result of the execution of ls, how do we read safely from it?

CodePudding user response:

wait(nullptr); 

This will halt the parent process until the child process terminates.

The child process is set up with its standard output hooked up to the write end of a pipe whose read end is opened in the parent process.

Pipes' internal buffer size is limited. If the child process generates enough output it will block until the pipe gets read from.

But the parent process is now waiting for the child process to terminate before doing anything. This will result in a deadlock.

Additionally, it looks like the parent process still has the write end of the pipe opened. If the parent process tries to read from it, and it reads everything, it will block because the write end of the pipe is still open (even after the child process has terminated).

So, no matter what you want to do, the write end of the pipe in the parent process should be closed.

From that point on you have several options available to you "conceptually" (as you asked).

You can focus on reading from the pipe. An end-of-file indication will inform you when the write end of the pipe has closed. The child process has terminated so you can wait() for it now, and get its immediate exit code.

This is probably the most common approach. Other approach are also possible, like both reading and keeping an eye on SIGPIPE. Or use signal file descriptors, on Linux, to catch the SIGPIPE in a manner that allows this to be conveniently multiplexed with reading from the pipe (via poll or select).

Note that if the child process terminated after writing the pipe, and the parent process has wait() for the child without reading from the pipe, after wait() returns it's possible to read the unread data from the pipe. It'll still be there. But, as I explained, this is fragile, and will break if the child process generates sufficient output to clog up the entire pipe.

It's often much simpler to read from the pipe, until an end-of-file indication and then clean up after the child process.

  • Related