当我在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缓冲区和管道都很新,所以我不知道我是否使用了正确的术语。
答案 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()
将解决问题的方法。