简体   繁体   中英

C Rewind FILE pointer coming from a command output

I have a problem with the output of a command, when I try to use rewind or fseek to star over again and check the output command to do other stuffs it doesn't work, this is the partial code:

#include <stdio.h>
#include <string.h>
#include <stdlib.h> 
int  main(){
     FILE *pf;
     int nhd=1;
     size_t b_size = 80;
     char *data = malloc(b_size * sizeof(char));
     pf = popen("/sbin/mycmd -s","r");

     while(-1 != getline(&data, &b_size, pf))
          nhd++;

     fseek (pf , 0 , SEEK_SET); // rewind(pf);

     while(-1 != getline(&data, &b_size, pf))
          nhd++;
     printf("%i\n",nhd);
     pclose(pf);
     free(data);
} 

It should output the double of number of the lines of the commands but rewind or what ever is not working as when I use a real file instead. What should I do?

Not all streams are seekable, for example those that have ephemeral content such as pipelines or sockets. As per C11 7.21.9.2 The fseek function /6 :

The fseek function returns nonzero only for a request that cannot be satisfied.

So you really need to check whether it's worked or not. And check it with fseek() rather than rewind() , the latter gives you no error indication.

One approach would be to read ten bytes up front then try to seek back to the beginning. If that works, carry on as if it's seekable. If not, complain bitterly and exit.

If the stream isn't seekable but you need to be able to back up, you'll need to store it yourself.

For example, even if the data is coming in on standard input from a pipe, there's nothing stopping you from writing that to a temporary file as you read it and using that file if you need to back up at some point.

It can become complicated in that you need to decide whether you're reading from the temporary file or reading new data coming in on the pipeline, but sometimes complexity is hard to avoid.

If you're really specifically trying to just double the output, and the fd is not a file, omit the fseek() and close the pipe and reopen it and do the read loop the second time. Also nhd should start at 0, not 1, to get an accurate count of lines read. If your goal is something more involved, then you should indicate where you're going with this. I'm not surprised fseek() doesn't work on non-files. The documentation refers to files, but ambiguously indicates 'stream' as well

UPDATE: Comments indicate the reason for double-pass at input is to pre-count to determine amount to allocate. Alternatively, use a linked list to dynamically allocate data with malloc(), avoiding the need to pre-count/pre-allocate:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE 256

typedef struct list {
    struct list *next;
    char *string;
} LIST;

LIST *head, *p;

int 
main(int argc, argv) 
{
    FILE *fp;
    char line[MAX_LINE];

    if ((fp = fopen(argv[1], "r")) == NULL) {
         fprintf(stderr, "Error opening %s\n", argv[1]);
         exit(-1);
    }
    while(fgets(line, sizeof(line), fp)) {
        LIST *n = malloc(sizeof(LIST));  
        n->string = strdup(line);
        n->next = NULL;
        p = (head == NULL) ? head = n : p->next = n;
    }
    fclose(fp);

    for(p = head; p; p != NULL; p->next)
        printf("%s", p->string);

    // 
    // . . . do whatever
    // 

    /*
     * Free list when unneeded
     */
    for(p = head; p != NULL; ) {
        printf("%s", p->string);
        LIST *saved = p->next
        free(p->string);
        free(p);
        p = saved;
    }
    return 0;
}

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