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?
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.