如何使用pipe和scanf()读取命令输出?

时间:2018-11-24 18:12:39

标签: c linux pipe

我需要能够发送GET命令的输出并将其存储到程序内部的变量中,目前我正在这样做:

  

获取google.com | ./myprogram

并使用以下代码在我的程序中接收它:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(int argc, char *argv[]){
  char *a = (char *) malloc (10000000);
  scanf("%[^\n]", a);
  printf("%s\n",a);

return 0;
}

我的问题是scanf函数在新行处停止,我需要能够存储GET中的整个段落输出。

任何帮助将不胜感激。谢谢。

3 个答案:

答案 0 :(得分:1)

一种可能性:GET是否在标头中包含尺寸信息?您可以使用它来确定要分配多少空间以及要读取多少数据吗?不过,这很奇怪,需要读取点点滴滴的数据。

更简单地说,考虑使用POSIX(和Linux)getdelim()getline()的近亲)并将分隔符指定为空字节。这不太可能出现在GET输出中,因此整个内容将是单个“行”,并且getdelim()将自动分配适当数量的空间。它还可以告诉您数据有多长时间。

#include <stdio.h>

int main(void)
{
    char *buffer = 0;
    size_t buflen = 0;
    int length = getdelim(&buffer, &buflen, '\0', stdin);
    if (length > 0)
        printf("%*.*s\n", length, length, buffer);
    free(buffer);
    return 0;
}

答案 1 :(得分:0)

scanf文档说

  

这些函数成功返回输入项的数量   匹配和分配,可能少于提供的数量,甚至   如果早期匹配失败,则为零。值EOF为   如果在第一个输入之前到达输入结束,则返回   转换成功或匹配失败。 EOF也是   如果发生读取错误,则返回,在这种情况下,错误指示符为   设置了流(请参阅ferror(3)),并且设置了errno表示   错误。

https://www.freebsd.org/cgi/man.cgi?query=scanf&sektion=3

您是否考虑过编写一个调用scanf的循环,监视其返回值并在EOF时中断

答案 2 :(得分:0)

考虑以下在标准C中实现的readall()函数:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

char *readall(FILE *source, size_t *length)
{
    char   *data = NULL;
    size_t  size = 0;
    size_t  used = 0;
    size_t  n;

    /* If we have a place to store the length,
       we initialize it to zero. */
    if (length)
        *length = 0;

    /* Do not attempt to read the source, if it is
       already in end-of-file or error state. */
    if (feof(source) || ferror(source))
        return NULL;

    while (1) {

        /* Ensure there is unused chars in data. */
        if (used >= size) {
            const size_t  new_size = (used | 65535) + 65537 - 32;
            char         *new_data;

            new_data = realloc(data, new_size);
            if (!new_data) {
                /* Although reallocation failed, data is still there. */
                free(data);
                /* We just fail. */
                return NULL;
            }

            data = new_data;
            size = new_size;
        }

        /* Read more of the source. */
        n = fread(data + used, 1, size - used, source);
        if (!n)
            break;

        used += n;
    }

    /* Read error or other wonkiness? */
    if (!feof(source) || ferror(source)) {
        free(data);
        return NULL;
    }

    /* Optimize the allocation. For ease of use, we append
       at least one nul byte ('\0') at end. */
    {
        const size_t  new_size = (used | 7) + 9;
        char         *new_data;

        new_data = realloc(data, new_size);
        if (!new_data) {
            if (used >= size) {
                /* There is no room for the nul. We fail. */
                free(data);
                return NULL;
            }
            /* There is enough room for at least one nul,
               so no reason to fail. */
        } else {
            data = new_data;
            size = new_size;
        }
    }

    /* Ensure the buffer is padded with nuls. */
    memset(data + used, 0, size - used);

    /* Save length, if requested. */
    if (length)
        *length = used;

    return data;
}

它将所有内容从指定的文件句柄(可以是标准流,例如stdin或通过popen()打开的管道)读取到动态分配的缓冲区中,并附加一个空字节({{1} }),并返回指向缓冲区的指针。如果不为NULL,则读取的实际字符数(因此,不包括附加的nul字节)将存储在第二个参数指向的\0中。

您可以使用它来读取程序(例如size_t或图像转换器)甚至是dot -Tpng diagram.dot输出(从特定的URL,文本或二进制文件中获取数据)输出的二进制数据。

您可以这样使用它,例如:

wget -O -

int main(void) { char *src; size_t len; src = readall(stdin, &len); if (!src) { fprintf(stderr, "Error reading standard input.\n"); return EXIT_FAILURE; } fprintf(stderr, "Read %zu chars.\n", len); /* As an example, print it to standard output. */ if (len > 0) fwrite(src, len, 1, stdout); free(src); return EXIT_SUCCESS; } 函数有两个怪癖:它以大约131072字节的块分配内存(但如果readall()返回短读,则可能有所不同),并用7到15个nul字节填充缓冲区。 (我之所以喜欢这样,是有原因的,但是这都是推测性的,并且是针对我倾向于使用的C库的,因此并不重要。)

尽管上面使用的方法可以正常工作,但是如果您愿意,可以更改fread()的计算。只要确保它们都至少为size_new