如何将管道传递给C应用程序作为文件?

时间:2018-02-13 00:33:36

标签: c++ c file pipe file-descriptor

我想做这样的事情:

nc localhost 1234 | myapp

但我希望myapp能够看到从命令的第一部分传递的管道作为文件。

例如:

int main() {
    File *fp;
    fp = /* get pointer file which is the pipe passed as "nc localhost 1234" */
}

这可能吗?

感谢。

3 个答案:

答案 0 :(得分:3)

当您的程序作为管道的一部分启动时,标准输出 以前的流程通过管道传输到程序的标准输入。所以你可以 做:

int main(void)
{
    char line[1024];
    fgets(line, sizeof line, stdin);
    printf("First line is: %s\n", line);

    return 0;
}

在POSIX系统上,您可以使用isatty检查文件描述符是否属于 是指终端。例如:

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

int main(void)
{
    if(isatty(STDIN_FILENO))
        puts("stdin connected to terminal");
    else
        puts("piped data to stdin");

    return 0;
}

程序的标准行为是它们从stdin读取并写入 stdout。坚持这一点,你可以使用shell链接你的程序 管|

例如,如果没有传递文件,cat期望从标准输入读取 作为一个论点。 cat的一个简单实现是:

int main(int argc, char **argv)
{
    FILE *in = stdin;

    if(argc == 2)
    {
        in = fopen(argv[1], "r");
        if(in == NULL)
        {
            perror("fopen");
            return 1;
        }
    }

    int c;
    while((c = fgetc(in)) != EOF)
        fputc(c, stdout);

    if(in != stdin)
        fclose(in);

    return 0;
}

作为一名程序员,我在阅读stdin时是否真的不在意 用户键入终端中的输入,或输入是否是管道输入。相同 写给stdout的事情是正确的。作为用户,我知道像cat这样的程序 没有文件传递时从stdin读取并写入stdout。因为 我可以做的这种行为

$ echo "hi" | cat | sort

所以你也应该模仿这种行为。

当然,有时候知道是否涉及管道是件好事。例如 当您这样做时,git diff会使用颜色和没有颜色的stdout写入 git diff | more。在这种情况下,可以检查stdout的连接位置 写给stdout的行为是一样的。

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

void print_colored_message(const char *msg)
{
    printf("\x1b[31m%s\x1b[0m\n", msg);
}

void print_message(const char *msg)
{
    if(isatty(STDOUT_FILENO))
        print_colored_message(msg);
    else
        puts(msg);
}

int main(void)
{
    print_message("Hello");
    print_message("World");

    return 0;
}

复制&amp;粘贴此程序并以./test执行一次,./test | less执行一次。 你会看到在第一个版本中,你得到一个彩色输出,而在 第二个版本,你没有。保持不变的是输出完成了 stdout

如果您不希望以这种方式链接您的程序,那么您必须告诉 您的用户不应该在这样的链中使用您的程序。

答案 1 :(得分:2)

您是否考虑使用命名管道?它们就像管道,一个方向,但它们有名字。如果你想要两个方向,你可以制作两个管道。你制作这样的管道:

mkfifo /dir/to_prog
mkfifo /dir/from_prog

然后一个程序的输出可以单独通过管道连接到第二个,管道名称可以像普通文件名一样使用:

nc localhost 1234 > /dir/to_prog
myapp -i /dir/to_prog -o /dir/from_prog
cat < /dir/from_prog

除了这三个命令甚至可以在三个单独的shell中发出。 myapp.c看起来像这样:

#include <stdio.h>

int
main(int argc, char *argv[])
{
    int iFd, oFd, opt;

    while (opt = getopt(argc, argv, "i:o:")) != -1) {

        switch (opt) {

        ...

        case 'i':  iFd = open(optarg, O_RDONLY); break;
        case 'o':  oFd = open(optarg, O_WRONLY); break;

        ...
        }
    }

    ...

    read(iFd, inBuf);

    ...

    write(oFd, outBuf);

    ...
}

当我需要能够处理程序中的数据流时,我已经使用过这种方法几次,因为某些原因还需要保留它自己的STDIN或STDOUT。如果您希望myprog能够从两个程序写入的管道中读取数据,那么唯一棘手的部分就是:

cat afile > /dir/to_prog
cat anotherFile > /dir/to_prog

让myprog能够读取这两个文件。问题是,当第一只猫退出时,它会关闭/ dir / to_prog,而myprog会把它读作EOF。许多程序将EOF解释为“没有更多的数据时间退出!”但是在这种情况下,一旦第二只猫启动,myprog将能够从/ dir / to_prog中读取更多数据,它只需要知道在看到EOF时不要放弃阅读。当然,如果两个程序同时写入命名管道(或从中读取),它们的数据将被随机交错。

答案 2 :(得分:1)

从BASH您可以使用功能temporary fifo

myapp <( nc localhost 1234 )

从C开始,如果你想将标准输入作为FILE指针读取,那么只需使用stdin

#include <stdio.h>

int main() {
    FILE *fp;
    fp = stdin;
}

如果要直接在C中重定向自定义命令的输出,请使用popen