C 倒回来自命令输出的 FILE 指针

C Rewind FILE pointer coming from a command output

我的命令输出有问题,当我尝试使用 rewind 或 fseek 重新启动并检查输出命令以执行其他操作时它不起作用,这是部分代码:

#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);
} 

它应该输出双倍的命令行数,但倒带或什么都不起作用,因为当我使用真实文件时。我该怎么办?

并非所有流都是可搜索的,例如那些具有临时内容的流,例如管道或套接字。根据 C11 7.21.9.2 The fseek function /6:

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

所以你真的需要检查它是否有效。并用 fseek() 而不是 rewind() 检查它,后者不会给你任何错误指示。

一种方法是先读取十个字节,然后尝试返回到开头。如果可行,请继续进行,就好像它是可搜索的一样。不行就狠狠地吐槽退出

如果流可搜索但您需要能够备份,您需要自己存储它.

例如,即使数据是从管道通过标准输入输入的,也没有什么可以阻止您在读取时将其写入临时文件,并在需要备份时使用该文件.

这可能会变得很复杂,因为您需要决定是从临时文件读取还是读取管道中传入的新数据,但有时很难避免复杂性。

如果您真的只是专门尝试将输出加倍,并且 fd 不是文件,请省略 fseek() 并关闭管道并重新打开它并第二次执行读取循环。 nhd 也应该从 0 而不是 1 开始,以获得准确的读取行数。如果您的目标涉及更多,那么您应该指出您的目标。我对 fseek() 对非文件不起作用并不感到惊讶。文档引用文件,但含糊地指出 'stream' 以及

更新:评论表明在输入时进行两次传递的原因是为了预先计算以确定要分配的数量。或者,使用链表通过 malloc() 动态分配数据,避免需要 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;
}