简体   繁体   中英

How to output to a file with information from a pipe in C?

I'm confused about what I'm doing wrong when I'm attempting to output to a file after I've execed a second program.

Say I have input file that has the following names:

Marty B. Beach 7 8
zachary b. Whitaker 12 23
Ivan sanchez 02 15
Jim Toolonganame 9 03

After my programs finish, it will convert the student's names to their usernames and output it to a file such as this:

mbb0708
zbw1223
is0215
jt0903

Currently as my program stands, it outputs nothing to the file and the terminal seems to be in an infinite loop despite self testing my converter program before and making sure it outputs names correctly to stdout.

I'm not sure what I'm doing wrong here? First time programming with pipes. I know have to make use of the read and write commands extract the data, but with the dup2 command is that necessary for the read command alone?

manager.c

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(int argc, char** argv)
{
    pid_t pid;

    int nbytes;

    /*Buffer to hold data from pipe*/
    char buffer[BUFSIZ + 1];

    /*Pipe Information*/
    int commpipe[2];
    if(pipe(commpipe))
    {
        fprintf(stderr, "Pipe failed.\n");
        return EXIT_FAILURE;
    }

    if((pid = fork()) == -1)
    {
        fprintf(stderr,"Fork error. Exiting.\n"); 
        exit(1);
    }
    else if(pid == 0)
    {
        /*This is the child process. Close our copy of the write end of the file descriptor.*/
        close(commpipe[1]);
        /* Connect the read end of the pipe to standard input*/
        dup2(commpipe[0], STDIN_FILENO);
        /*Program will convert the Student's name to their respective names*/
        execl("converter","converter",NULL);
        /*Exit if failure appears*/
        exit(EXIT_FAILURE);
    }
    else
    {
        FILE *file;
        file = fopen("usernames.txt","a+"); //append a file(add text to a file or create a file it does not exist)
        /*Close or copy of the read end of the file descriptor */
        //close(commpipe[1]);

        nbytes = write(commpipe[1], buffer, BUFSIZ);

        //Read from pipe here first? 

        //Output to usernames.txt the usernames of the user from the pipe.
        fprintf(file, "%s", buffer);

        /*Wait for the child process to finish*/
        waitpid(pid, NULL, 0);
    }

    return 0;
}

One problem is that after manager has sent all the data to converter, the manager is not closing commpipe[1] . Because of that, converter will never get EOF on stdin so will not exit.

Most likely manager isn't getting any data back from converter due to buffering. Some implementations of stdio use full-buffer buffering (as opposed to line-buffering) when not writing to a terminal. Once you fix the previous error and get the process to close, that will flush stdout. You can also consider adding fflush(stdout) after your puts line.

Have a look at the OpenGroup site, there's an example that looks similar to yours. I suggest you get the sample working first with some hard coded. Once that is working, add the code to read and write the results.

I made some minor changes to get the example working:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>

int main(int argc, char** argv){
   int fildes[2];
   const int BSIZE = 100;
   char buf[BSIZE];
   ssize_t nbytes;
   int status;


   status = pipe(fildes);
   if (status == -1 ) {
       /* an error occurred */
       printf("Error!\n");
       exit(-1);
   }

   printf("Forking!\n");
   switch (fork()) {
   case -1: /* Handle error */
       printf("Broken Handle :(\n");
       break;


   case 0:  /* Child - reads from pipe */
       printf("Child!\n");
       close(fildes[1]);                       /* Write end is unused */
       nbytes = read(fildes[0], buf, BSIZE);   /* Get data from pipe */
       /* At this point, a further read would see end of file ... */
       assert(nbytes < BSIZE);                 /* Prevent buffer overflow */
       buf[nbytes] = '\0';                     /* buf won't be NUL terminated */
       printf("Child received %s", buf);
       close(fildes[0]);                       /* Finished with pipe */
       fflush(stdout);
       exit(EXIT_SUCCESS);


   default:  /* Parent - writes to pipe */
       printf("Parent!\n");
       close(fildes[0]);                       /* Read end is unused */
       write(fildes[1], "Hello world\n", 12);  /* Write data on pipe */
       close(fildes[1]);                       /* Child will see EOF */
       /* Note that the Parent should wait for a response from the
       child here, because the child process will be terminated once
       the parent exits */
       exit(EXIT_SUCCESS);
   }

   return 0;
}

As I understand, your converter program reads lines from stdin and writes them to stdout. As a pipe is a uni-directional entity, you will need TWO of them to communicate with the manager - one to send data to the converter and one to receive output from it.

Maybe you should consider enhancing the converter to take (as optional arguments) the name of an input and output file.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM