C++ 启动脚本(bash、python ...)并使用 stdin/stdout 进行数据传输 [linux]
C++ start a script ( bash, python ... ) and using stdin/stdout for datatransfare [linux]
我想从我的 C++ 代码调用 python 脚本。 python 脚本如下所示:
hello.py
#!/usr/bin/python
import sys
print "Hello!"
Readin = sys.stdin.read()
print Readin
C++ 代码来自堆栈溢出的另一个问题。它应该如何工作:
正在创建一对管道。
正在使用 fork()
创建子进程。
child 正在将其管道弯曲到标准输入/标准输出。关闭其他端
并启动脚本。
父亲 正在监听管道 read()
,接收输入。之后,
发送消息 write()
.
当输入 switch (readResult = read(childToPa...
时,程序不会从 fathers 行 return。
我也不知道这个写作部分是否完成了它的工作。这样做是一个有前途的想法,还是有其他可行的方法?
谢谢!
看起来像:
// maybe not all includes are necessary
#include <iostream>
#include <fstream>
#include <string>
#include <errno.h>
#include <sys/stat.h> // mkdir
#include <stdlib.h> // system()
#include <unistd.h> // rmdir
#include <cstring> // memset
// wait:
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main() {
char target[] = "./hello.py";
enum PIPE_FILE_DESCRIPTERS {
READ_FD = 0, WRITE_FD = 1
};
enum CONSTANTS {
BUFFER_SIZE = 100
};
int parentToChild[2];
int childToParent[2];
pid_t pid;
string dataReadFromChild;
char buffer[BUFFER_SIZE + 1];
memset(buffer,0x00,BUFFER_SIZE + 1);
ssize_t readResult;
int status;
int retPipe1 = pipe(parentToChild);
int retPipe2 = pipe(childToParent);
switch (pid = fork()) {
case -1:
printf("Fork failed");
exit(-1);
case 0: /* Child will start scripts*/
{
// Bending stdin/out to the pipes?
int retdup21 = dup2(parentToChild[READ_FD], STDIN_FILENO);
int retdup22 = dup2(childToParent[WRITE_FD], STDOUT_FILENO);
int retdup23 = dup2(childToParent[WRITE_FD], STDERR_FILENO);
// Close in this Process the other sides of the pipe
int retclose1 = close(parentToChild[WRITE_FD]);
int retclose2 = close(childToParent[READ_FD]);
int retexe = execlp( target ," "); // warning not enough variable arguments to fit a sentinel [-Wformat=]
printf("This line should never be reached!!!"); // why? what happens if execlp finishes?
exit(-1);
break; // to make the compiler happy =)
}
default: /* Parent */
cout << "Child " << pid << " process running..." << endl;
// close the other ends of the pipe from the other process.
int retdup21 = close(parentToChild[READ_FD]);
int retdup22 = close(childToParent[WRITE_FD]);
// readtry
while (true) {
switch (readResult = read(childToParent[READ_FD], buffer, 1)) // execution does not return from this function.
{
case 0: /* End-of-File, or non-blocking read. */
{
cout << "End of file reached..." << endl << "Data received was (" << dataReadFromChild.size() << "):" << endl
<< dataReadFromChild << endl;
cout << "starting writing" << endl;
char bufferW[] = "{\"AElement\":\"Something\"}[=15=]";
int writeResult = write(parentToChild[WRITE_FD],bufferW,sizeof(bufferW));
int saveerrno = errno;
if( -1 == writeResult)
{
cout << "errno while writing: " << errno << std::endl;
if ( 9 == saveerrno )
cout << "Errno Bad File descriptor" << endl;
}
cout << "Write Result: " << writeResult << std::endl;
int retWait = waitpid(pid, &status, 0);
cout << endl << "Child exit staus is: " << WEXITSTATUS(status) << endl << endl;
exit(0);
}
case -1:
{
if ((errno == EINTR) || (errno == EAGAIN)) {
errno = 0;
break;
} else {
printf("read() failed");
exit(-1);
}
}
default:
dataReadFromChild.append(buffer, readResult);
printf("%s",buffer);
memset(buffer,0x00,BUFFER_SIZE + 1);
break;
}
} /* while ( true ) */
} /* switch ( pid = fork() )*/
}
您的问题是缓冲输出、未关闭的文件描述符以及使用 EOF 表示传输的 部分 结束。前两个问题可以解决,但最后一个问题需要不同的方法。稍后会详细介绍。
一步一步:
Python 使用缓冲 I/O,因此您可能希望通过在第一个打印语句后添加行 sys.stdout.flush()
来强制 Python 刷新输出。现在 "Hello!\n"
是逐个字符读取的。
但是下一个 read
会阻塞,直到新角色到达或管道关闭。 Python 脚本的 STDOUT 仍然打开,C++ 程序正在等待一些东西到达,但 Python 脚本本身也在等待一些输入。经典死锁。
您可以放弃 python 脚本中的最后打印并尝试关闭其 STDOUT。由于 read
阻塞直到引用管道写入端的所有文件描述符都关闭,因此您必须在 flush
.
之后添加 os.close(sys.stdout.fileno())
和 os.close(sys.stdout.fileno())
但是仍然有有效的文件描述符引用该管道的写入部分。还记得 C++ 源代码中的 dup2
吗?在这三个 dup2
行之后,还有 parentToChild[READ_FD]
和 childToParent[WRITE_FD]
引用脚本 STDIN 和 STDOUT。所以我们必须关闭它们。在 dup2
之后添加 close(parentToChild[READ_FD]);
和 close(childToParent[WRITE_FD]);
。现在 read
returns 0
当 Python 脚本关闭 STDOUT 和 STDERR 时。
接下来,parent 发送 "{\"AElement\":\"Something\"}[=28=]"
并到达 waitpid
,当 child 退出时 returns。但是 child 仍在从 STDIN 读取。所以你必须在 waitpid
.
之前添加 close(parentToChild[WRITE_FD]);
现在是概念部分:您不能read()
直到它returns一个0
(管道关闭),然后继续从那个关闭的管道读取。您的选择:
- 读取一次直到管道关闭。不可能有第二条消息。
- 知道要读多少。提前或通过解释接收到的字节。
- 监控两个管道,例如使用 poll(2) 并动态决定是否要读取或写入。
顺便说一句:execlp
的参数是 const char *file, const char *arg, ...
,其中 arg, ...
是通常的 char *args[]
,以 arg[0]
(!) 开头并以 a 结尾空指针。请将该行更改为 int retexe = execlp(target, target, (char*) NULL);
#!/usr/bin/python2.7
import os
import sys
print "Hello!"
sys.stdout.flush()
os.close(sys.stdout.fileno())
os.close(sys.stderr.fileno())
data = sys.stdin.read()
with open("data_received_by_child.txt", "w") as fp:
print >>fp, data
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
int main()
{
const char *target = "./hello.py";
enum PIPE_FILE_DESCRIPTERS {
READ_FD = 0, WRITE_FD = 1
};
/* Make pipes */
int parentToChild[2]; /* Parent to child pipe */
if (pipe(parentToChild) < 0)
{
perror("Can't make pipe");
exit(1);
}
int childToParent[2]; /* Child to parent pipe */
if (pipe(childToParent) < 0)
{
perror("Can't make pipe");
exit(1);
}
/* Create a child to run command. */
pid_t pid = fork();
switch (pid)
{
case -1:
perror("Can't fork");
exit(1);
case 0: /* Child */
close(parentToChild[WRITE_FD]);
close(childToParent[READ_FD]);
dup2(parentToChild[READ_FD], STDIN_FILENO);
dup2(childToParent[WRITE_FD], STDOUT_FILENO);
close(parentToChild[READ_FD]);
close(childToParent[WRITE_FD]);
execlp(target, target, (char *) NULL);
perror("Can't execute target");
exit(1);
default: /* Parent */
close(parentToChild[READ_FD]);
close(childToParent[WRITE_FD]);
cout << "Child " << pid << " process running..." << endl;
}
/* Read data from child */
string dataReadFromChild;
char ch;
int rc;
while ((rc = read(childToParent[READ_FD], &ch, 1)) != 0)
{
if (rc == -1) {
if ((errno == EINTR) || (errno == EAGAIN)) {
continue;
}
perror("read() failed");
exit(-1);
}
dataReadFromChild += ch;
}
close(childToParent[READ_FD]);
cout << "End of file reached..." << endl;
cout << "Data received was (" << dataReadFromChild.size() << "):" << endl;
cout << dataReadFromChild << endl;
/* Write data to child */
cout << "starting writing" << endl;
const char bufferW[] = "{\"AElement\":\"Something\"}[=11=]";
while (true) {
int rc = write(parentToChild[WRITE_FD], bufferW, sizeof(bufferW));
if (rc == -1) {
if ((errno == EINTR) || (errno == EAGAIN)) {
continue;
}
perror("write() failed");
exit(-1);
}
break;
}
close(parentToChild[WRITE_FD]);
/* Wait for child to exit */
int status;
int retWait = waitpid(pid, &status, 0);
cout << endl << "Child exit status is: " << WEXITSTATUS(status) << endl << endl;
}
我想从我的 C++ 代码调用 python 脚本。 python 脚本如下所示:
hello.py
#!/usr/bin/python
import sys
print "Hello!"
Readin = sys.stdin.read()
print Readin
C++ 代码来自堆栈溢出的另一个问题。它应该如何工作:
正在创建一对管道。
正在使用
fork()
创建子进程。child 正在将其管道弯曲到标准输入/标准输出。关闭其他端 并启动脚本。
父亲 正在监听管道
read()
,接收输入。之后, 发送消息write()
.
当输入 switch (readResult = read(childToPa...
时,程序不会从 fathers 行 return。
我也不知道这个写作部分是否完成了它的工作。这样做是一个有前途的想法,还是有其他可行的方法? 谢谢!
看起来像:
// maybe not all includes are necessary
#include <iostream>
#include <fstream>
#include <string>
#include <errno.h>
#include <sys/stat.h> // mkdir
#include <stdlib.h> // system()
#include <unistd.h> // rmdir
#include <cstring> // memset
// wait:
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main() {
char target[] = "./hello.py";
enum PIPE_FILE_DESCRIPTERS {
READ_FD = 0, WRITE_FD = 1
};
enum CONSTANTS {
BUFFER_SIZE = 100
};
int parentToChild[2];
int childToParent[2];
pid_t pid;
string dataReadFromChild;
char buffer[BUFFER_SIZE + 1];
memset(buffer,0x00,BUFFER_SIZE + 1);
ssize_t readResult;
int status;
int retPipe1 = pipe(parentToChild);
int retPipe2 = pipe(childToParent);
switch (pid = fork()) {
case -1:
printf("Fork failed");
exit(-1);
case 0: /* Child will start scripts*/
{
// Bending stdin/out to the pipes?
int retdup21 = dup2(parentToChild[READ_FD], STDIN_FILENO);
int retdup22 = dup2(childToParent[WRITE_FD], STDOUT_FILENO);
int retdup23 = dup2(childToParent[WRITE_FD], STDERR_FILENO);
// Close in this Process the other sides of the pipe
int retclose1 = close(parentToChild[WRITE_FD]);
int retclose2 = close(childToParent[READ_FD]);
int retexe = execlp( target ," "); // warning not enough variable arguments to fit a sentinel [-Wformat=]
printf("This line should never be reached!!!"); // why? what happens if execlp finishes?
exit(-1);
break; // to make the compiler happy =)
}
default: /* Parent */
cout << "Child " << pid << " process running..." << endl;
// close the other ends of the pipe from the other process.
int retdup21 = close(parentToChild[READ_FD]);
int retdup22 = close(childToParent[WRITE_FD]);
// readtry
while (true) {
switch (readResult = read(childToParent[READ_FD], buffer, 1)) // execution does not return from this function.
{
case 0: /* End-of-File, or non-blocking read. */
{
cout << "End of file reached..." << endl << "Data received was (" << dataReadFromChild.size() << "):" << endl
<< dataReadFromChild << endl;
cout << "starting writing" << endl;
char bufferW[] = "{\"AElement\":\"Something\"}[=15=]";
int writeResult = write(parentToChild[WRITE_FD],bufferW,sizeof(bufferW));
int saveerrno = errno;
if( -1 == writeResult)
{
cout << "errno while writing: " << errno << std::endl;
if ( 9 == saveerrno )
cout << "Errno Bad File descriptor" << endl;
}
cout << "Write Result: " << writeResult << std::endl;
int retWait = waitpid(pid, &status, 0);
cout << endl << "Child exit staus is: " << WEXITSTATUS(status) << endl << endl;
exit(0);
}
case -1:
{
if ((errno == EINTR) || (errno == EAGAIN)) {
errno = 0;
break;
} else {
printf("read() failed");
exit(-1);
}
}
default:
dataReadFromChild.append(buffer, readResult);
printf("%s",buffer);
memset(buffer,0x00,BUFFER_SIZE + 1);
break;
}
} /* while ( true ) */
} /* switch ( pid = fork() )*/
}
您的问题是缓冲输出、未关闭的文件描述符以及使用 EOF 表示传输的 部分 结束。前两个问题可以解决,但最后一个问题需要不同的方法。稍后会详细介绍。
一步一步:
Python 使用缓冲 I/O,因此您可能希望通过在第一个打印语句后添加行 sys.stdout.flush()
来强制 Python 刷新输出。现在 "Hello!\n"
是逐个字符读取的。
但是下一个 read
会阻塞,直到新角色到达或管道关闭。 Python 脚本的 STDOUT 仍然打开,C++ 程序正在等待一些东西到达,但 Python 脚本本身也在等待一些输入。经典死锁。
您可以放弃 python 脚本中的最后打印并尝试关闭其 STDOUT。由于 read
阻塞直到引用管道写入端的所有文件描述符都关闭,因此您必须在 flush
.
os.close(sys.stdout.fileno())
和 os.close(sys.stdout.fileno())
但是仍然有有效的文件描述符引用该管道的写入部分。还记得 C++ 源代码中的 dup2
吗?在这三个 dup2
行之后,还有 parentToChild[READ_FD]
和 childToParent[WRITE_FD]
引用脚本 STDIN 和 STDOUT。所以我们必须关闭它们。在 dup2
之后添加 close(parentToChild[READ_FD]);
和 close(childToParent[WRITE_FD]);
。现在 read
returns 0
当 Python 脚本关闭 STDOUT 和 STDERR 时。
接下来,parent 发送 "{\"AElement\":\"Something\"}[=28=]"
并到达 waitpid
,当 child 退出时 returns。但是 child 仍在从 STDIN 读取。所以你必须在 waitpid
.
close(parentToChild[WRITE_FD]);
现在是概念部分:您不能read()
直到它returns一个0
(管道关闭),然后继续从那个关闭的管道读取。您的选择:
- 读取一次直到管道关闭。不可能有第二条消息。
- 知道要读多少。提前或通过解释接收到的字节。
- 监控两个管道,例如使用 poll(2) 并动态决定是否要读取或写入。
顺便说一句:execlp
的参数是 const char *file, const char *arg, ...
,其中 arg, ...
是通常的 char *args[]
,以 arg[0]
(!) 开头并以 a 结尾空指针。请将该行更改为 int retexe = execlp(target, target, (char*) NULL);
#!/usr/bin/python2.7
import os
import sys
print "Hello!"
sys.stdout.flush()
os.close(sys.stdout.fileno())
os.close(sys.stderr.fileno())
data = sys.stdin.read()
with open("data_received_by_child.txt", "w") as fp:
print >>fp, data
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
int main()
{
const char *target = "./hello.py";
enum PIPE_FILE_DESCRIPTERS {
READ_FD = 0, WRITE_FD = 1
};
/* Make pipes */
int parentToChild[2]; /* Parent to child pipe */
if (pipe(parentToChild) < 0)
{
perror("Can't make pipe");
exit(1);
}
int childToParent[2]; /* Child to parent pipe */
if (pipe(childToParent) < 0)
{
perror("Can't make pipe");
exit(1);
}
/* Create a child to run command. */
pid_t pid = fork();
switch (pid)
{
case -1:
perror("Can't fork");
exit(1);
case 0: /* Child */
close(parentToChild[WRITE_FD]);
close(childToParent[READ_FD]);
dup2(parentToChild[READ_FD], STDIN_FILENO);
dup2(childToParent[WRITE_FD], STDOUT_FILENO);
close(parentToChild[READ_FD]);
close(childToParent[WRITE_FD]);
execlp(target, target, (char *) NULL);
perror("Can't execute target");
exit(1);
default: /* Parent */
close(parentToChild[READ_FD]);
close(childToParent[WRITE_FD]);
cout << "Child " << pid << " process running..." << endl;
}
/* Read data from child */
string dataReadFromChild;
char ch;
int rc;
while ((rc = read(childToParent[READ_FD], &ch, 1)) != 0)
{
if (rc == -1) {
if ((errno == EINTR) || (errno == EAGAIN)) {
continue;
}
perror("read() failed");
exit(-1);
}
dataReadFromChild += ch;
}
close(childToParent[READ_FD]);
cout << "End of file reached..." << endl;
cout << "Data received was (" << dataReadFromChild.size() << "):" << endl;
cout << dataReadFromChild << endl;
/* Write data to child */
cout << "starting writing" << endl;
const char bufferW[] = "{\"AElement\":\"Something\"}[=11=]";
while (true) {
int rc = write(parentToChild[WRITE_FD], bufferW, sizeof(bufferW));
if (rc == -1) {
if ((errno == EINTR) || (errno == EAGAIN)) {
continue;
}
perror("write() failed");
exit(-1);
}
break;
}
close(parentToChild[WRITE_FD]);
/* Wait for child to exit */
int status;
int retWait = waitpid(pid, &status, 0);
cout << endl << "Child exit status is: " << WEXITSTATUS(status) << endl << endl;
}