最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c - Reading from a pipe immediately after opening it - Stack Overflow

programmeradmin5浏览0评论

I wanted to launch an executable from a parent process. Wait until that process is finished, then read back what it wrote into stdout. It turns out you can't wait for the program to finish and then read the FILE* stream, because to wait for it to finish means to calls pclose(FILE*) and by that point the file stream is destroyed. In the Windows documentation it says to do this:

char psBuffer[128];
    FILE* pPipe;

    /* Run DIR so that it writes its output to a pipe. Open this
     * pipe with read text attribute so that we can read it
     * like a text file.
     */

    if ((pPipe = _popen("dir *.c /on /p", "rt")) == NULL)
    {
        exit(1);
    }

    /* Read pipe until end of file, or an error occurs. */

    while (fgets(psBuffer, 128, pPipe))
    {
        puts(psBuffer);
    }

    int endOfFileVal = feof(pPipe);
    int closeReturnVal = _pclose(pPipe);

    if (endOfFileVal)
    {
        printf("\nProcess returned %d\n", closeReturnVal);
    }
    else
    {
        printf("Error: Failed to read the pipe to the end.\n");
    }

It's opening the pipe, then immediately reading from the stream. Isn't it the case that it's likely it'll read the stream and it'll be empty or end of file since the read is executed immediately after starting the child process? Why is this valid?

I wanted to launch an executable from a parent process. Wait until that process is finished, then read back what it wrote into stdout. It turns out you can't wait for the program to finish and then read the FILE* stream, because to wait for it to finish means to calls pclose(FILE*) and by that point the file stream is destroyed. In the Windows documentation it says to do this:

char psBuffer[128];
    FILE* pPipe;

    /* Run DIR so that it writes its output to a pipe. Open this
     * pipe with read text attribute so that we can read it
     * like a text file.
     */

    if ((pPipe = _popen("dir *.c /on /p", "rt")) == NULL)
    {
        exit(1);
    }

    /* Read pipe until end of file, or an error occurs. */

    while (fgets(psBuffer, 128, pPipe))
    {
        puts(psBuffer);
    }

    int endOfFileVal = feof(pPipe);
    int closeReturnVal = _pclose(pPipe);

    if (endOfFileVal)
    {
        printf("\nProcess returned %d\n", closeReturnVal);
    }
    else
    {
        printf("Error: Failed to read the pipe to the end.\n");
    }

It's opening the pipe, then immediately reading from the stream. Isn't it the case that it's likely it'll read the stream and it'll be empty or end of file since the read is executed immediately after starting the child process? Why is this valid?

Share Improve this question edited Mar 22 at 13:57 Barmar 784k57 gold badges548 silver badges660 bronze badges asked Mar 22 at 8:54 ZebrafishZebrafish 15k3 gold badges66 silver badges153 bronze badges 9
  • As long as the stdout stream of the child process remains open, then the stdin of the parent will contine to try and read from it. If there is no data presently available then the stream will block until more data is sent. – OldBoy Commented Mar 22 at 9:39
  • @OldBoy Ah, that's what I didn't know, it blocks. All is good then. – Zebrafish Commented Mar 22 at 9:41
  • You can also redirect stdout to a regular file, then read it after the process completes. – stark Commented Mar 22 at 11:19
  • Indeed, even if the stdio functions did not block, they still would fail and set the stream's EOF indicator if they try to read, there is no more data to consume, and the other end of the pipe is closed. This is the normal way for a process consuming output of another through a pipe to recognize that the other has terminated. – John Bollinger Commented Mar 22 at 12:46
  • Side note: the question title says "socket", but popen() provides for a pipe. These are not the same thing. Pipes are unidirectional, whereas sockets are full-duplex bidirectional. – John Bollinger Commented Mar 22 at 12:48
 |  Show 4 more comments

1 Answer 1

Reset to default 3

It turns out you can't wait for the program to finish and then read the FILE* stream, because to wait for it to finish means to calls pclose(FILE*)

Sort of. Calling pclose() does wait for a child process started via popen() to terminate, and this is the only standard way to get the child's exit status. Having called pclose(), it is no longer possible to read from the stream that was closed.

But if the child does not close its stdout before terminating -- and most don't -- then the parent can detect EOF on the associated stream as a proxy for termination, prior to calling pclose(). That is natural for a parent that wants to read the child's output to the end. And with popen(), the parent almost always does want to do so, because even if it is not interested in the data itself, it must drain the pipe to ensure that the child continues to have space to write. Pipes have limited capacity, generally measured in kilobytes, and if a process tries to write when there is insufficient space available then either it will block until it can complete its write* (usual case) or it will fail in some idiosyncratic manner.

So no, you cannot wait for the program and then read its output from the stream, but you generally can wait for the program to finish by reading its output from the stream. Once you detect EOF on the stream, you call pclose() to retrieve the exit status and clean up, plus that covers you in case the child does close its standard output early.

It's opening the pipe, then immediately reading from the stream. Isn't it the case that it's likely it'll read the stream and it'll be empty or end of file since the read is executed immediately after starting the child process?

Empty? Quite possibly. But for a pipe, that's not the same thing as being at end of file. EOF does not occur on the read end of a pipe until the write end has been closed and all data have been read from it.

Why is this valid?

The stdio functions block until they transfer the amount of data they are specified to do. fgets() in particular will read until it fills the provided buffer, it reads and transfers a logical newline, or it reaches EOF. That it may have to wait before there is anything to transfer, and that it might periodically have to wait again before it completes its transfer, is just par for the course.


* What it means to complete a write depends on the function performing the write. For the stdio functions that means writing all the specified bytes, but there are other I/O functions for which that is not necessarily the case.

发布评论

评论列表(0)

  1. 暂无评论