是否可以通过管道实现命令替换?

Is it possible to implement command substitution via pipeline?

例如,我们有$(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]

这段代码非常概念化,但我说得对吗?是否可以在子进程完成后从管道的写入端提取数据?剩下的就是将子字符串 ($(ls)) 替换为另一个子字符串(ls 本身的结果)。有错请指正

即使 pfd[0] 是一个有效的文件描述符,它指向一个包含 ls 执行结果的缓冲区,我们如何安全地从中读取?

wait(nullptr); 

这将暂停父进程,直到子进程终止。

子进程的标准输出连接到管道的写入端,管道的读取端在父进程中打开。

管道的内部缓冲区大小有限。如果子进程生成足够的输出,它将阻塞直到管道被读取。

但是父进程现在正在等待子进程终止后再做任何事情。这将导致死锁。

此外,看起来父进程仍然打开了管道的写端。如果父进程试图从它读取,并且它读取了所有内容,it 将阻塞,因为管道的写入端仍然打开(即使在子进程终止后)。

所以,不管你想做什么,父进程中管道的写端都要关闭。

从那时起,您“概念上”(如您所问)有多种选择。

你可以专注于从管道读取。当管道的写端关闭时,文件结束指示将通知您。子进程已终止,因此您现在可以 wait() 获取它的立即退出代码。

这可能是最常见的方法。其他方法也是可能的,比如阅读和关注 SIGPIPE。或者在 Linux 上使用信号文件描述符来捕获 SIGPIPE,以便通过从管道读取数据(通过 pollselect)方便地进行多路复用.

请注意,如果子进程在写入管道后终止,并且父进程为子进程提供了 wait() 而未从管道读取,则在 wait() returns 之后,可以从管道中读取未读数据。它仍然会在那里。但是,正如我所解释的,这是脆弱的,如果子进程产生足够的输出来堵塞整个管道,它就会崩溃。

从管道读取数据通常要简单得多,直到出现文件结束指示,然后在子进程之后进行清理。