为什么写入模式的管道会输出什么?

时间:2016-05-01 20:00:49

标签: c++ pipe

当我在c ++中使用管道时,我偶然发现了一些相当有趣的东西。

#include <cstdio>
#include <iostream>
#include <string>

int main()
{
    FILE *pystream = popen("python","w"); // Calling the python console

    fprintf(pystream,"print(2+3)"); // Making it do something   

    pclose(pystream); // Closing the pipe

    return 0;
}

此代码输出5.但为什么?并且可以在某处读取或存储“输出”吗? 我对C缓冲区和管道都很新,所以我不知道我是否使用了正确的术语。

2 个答案:

答案 0 :(得分:1)

当你这样写的时候,你有效地写入你刚开始的过程的stdin,在这种情况下是python REPL。在Linux上,python REPL直接获取表达式,即它没有输入。这是系统命令

read(0, "print(2+3)", 4096) = 10

如果您在终端中执行此操作,则终端会一次一个地读取每个字符,当它返回回车时,它会写一个换行符\n

read(0, "\r", 1)                        = 1
write(1, "\n", 1

然后执行计算并将结果写出

write(1, "5\n", 25

您通过传递终端并将数据直接写入python解释器的stdin。如果你想看看它如何轻易破解,请尝试使用此代码。

#include <cstdio>
#include <iostream>
#include <string>
int main()
{
    FILE *pystream = popen("python","w"); // Calling the python console
    fprintf(pystream,"print(2+3)"); // Making it do something   
    fprintf(pystream,"print(2+3)"); // Making it do something   
    pclose(pystream); // Closing the pipe
    return 0;
}

你会得到一个语法错误,为了使它工作stdin需要提供一个回车或换行来分隔两行,即添加一个回车...

fprintf(pystream,"print(2+3)\r");

答案 1 :(得分:0)

您正在执行的命令的标准输出连接到程序的标准输出,因此当Python写入其标准输出时,它也会显示在您的过程的标准输出上。

如果在运行Python之前有挂起的输出,则不会刷新,并且会在Python返回后显示。例如,

std::cout << "Hello";
endl

之前

(字符串中没有\n,没有popen()

std::cout << " World\n";

pclose()之后意味着您将在Hello World之前看到Python输出。

如果您想写入Python并在程序中阅读结果,则无法再使用popen()pclose()。相反,你需要使用pipe()两次(一个管道与Python交谈,一个管道从Python读取),你需要使用fork()exec()dup2() - 可能;否则dup() - 和close()使操作有效。您将在父进程中使用文件描述符,因此也会使用read()write()系统调用。

这些都是C函数(系统调用),而不是C ++函数。

此代码有效:

#include <unistd.h>
#include <cstdio>
#include <cstring>

int main()
{
    int p1[2];
    int p2[2];
    if (pipe(p1) != 0 || pipe(p2) != 0)
        return 1;
    int pid;
    if ((pid = fork()) < 0)
        return 1;
    if (pid == 0)
    {
        dup2(p1[0], STDIN_FILENO);
        dup2(p2[1], STDOUT_FILENO);
        close(p1[0]);
        close(p1[1]);
        close(p2[0]);
        close(p2[1]);
        execlp("python", "python", (char *)0);
        fprintf(stderr, "failed to exec python\n");
        return 1;
    }
    else
    {
        close(p1[0]);
        close(p2[1]);
        const char command[] = "print(2+3)\n";
        int len = strlen(command);
        if (write(p1[1], command, len) != len)
        {
            fprintf(stderr, "failed to write command to python\n");
            return 1;
        }
        close(p1[1]);
        char buffer[256];
        int nbytes;
        if ((nbytes = read(p2[0], buffer, sizeof(buffer))) <= 0)
        {
            fprintf(stderr, "failed to read response from python\n");
            return 1;
        }
        printf("Python said: (%d) [%.*s]\n", nbytes, nbytes, buffer);
        close(p2[0]);
        printf("Finished\n");
    }
    return 0;
}

坏消息是,在同步从Python读取响应时更改此代码以编写多个命令不起作用。 Python不会像输入终端那样单独处理每一行;它会在响应之前读取所有数据。您可以使用python -i解决此问题,但随后来自Python的提示会显示在stderr上。因此,您可以将其重定向到/dev/null以丢失它:

#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <cstring>

int main()
{
    int p1[2];
    int p2[2];
    if (pipe(p1) != 0 || pipe(p2) != 0)
        return 1;
    int pid;
    if ((pid = fork()) < 0)
        return 1;
    if (pid == 0)
    {
        dup2(p1[0], STDIN_FILENO);
        dup2(p2[1], STDOUT_FILENO);
        close(p1[0]);
        close(p1[1]);
        close(p2[0]);
        close(p2[1]);
        int dn = open("/dev/null", O_WRONLY);
        if (dn >= 0)
        {
            dup2(dn, STDERR_FILENO);
            close(dn);
        }
        execlp("python", "python", "-i", (char *)0);
        fprintf(stderr, "failed to exec python\n");
        return 1;
    }
    else
    {
        close(p1[0]);
        close(p2[1]);
        const char *commands[] =
        {
            "print(2+3)\n",
            "print(3+4)\n",
        };
        enum { NUM_COMMANDS = sizeof(commands) / sizeof(commands[0]) };
        for (int i = 0; i < NUM_COMMANDS; i++)
        {
            int len = strlen(commands[i]);
            if (write(p1[1], commands[i], len) != len)
            {
                fprintf(stderr, "failed to write command to python\n");
                return 1;
            }
            char buffer[256];
            int nbytes;
            if ((nbytes = read(p2[0], buffer, sizeof(buffer))) <= 0)
            {
                fprintf(stderr, "failed to read response from python\n");
                return 1;
            }
            printf("Python said: (%d) [%.*s]\n", nbytes, nbytes, buffer);
        }
        close(p1[1]);
        close(p2[0]);
        printf("Finished\n");
    }
    return 0;
}

没有重定向stderr

Python 2.7.10 (default, Oct 23 2015, 19:19:21) 
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> >>> Python said: (2) [5
]
>>> Python said: (2) [7
]
Finished

重定向stderr

Python said: (2) [5
]
Python said: (2) [7
]
Finished

将标准错误输出丢失到/dev/null的缺点是,当Python对象发送到执行时,您将不会得到任何通知 - 代码将挂起。解决这个问题很有趣(第三个管道,并使用poll()epoll()或 - 消除思想 - select()将解决问题的方法。