我有程序我无法修改,按,我需要执行它,将一些数据写入其标准输入并以编程方式从其标准输出中获得答案,自动化。 什么是最简单的方法?
我想像这样的伪C代码
char input_data_buffer[] = "calculate 2 + 2\nsay 'hello world!'";
char output_data_buffer[MAX_BUF];
IPCStream ipcs = executeIPC("./myprogram", "rw");
ipcs.write(input_data_buffer);
ipcs.read(output_data_buffer);
...
PS:我想过popen,但是AFAIK在linux中只有单向管道
修改
假设它将是来自每一方的一个消息通信。首先,父端向子进程'stdin发送输入,然后child向其stdout提供输出并退出,同时parent读取其stdout。现在关于通信终止:我认为当子进程退出时它会将EOF终结符发送到stdout,因此父进程将确切知道子进程是否完成,另一方面保证父进程知道子进程需要什么样的输入。
通常这个程序(父母) - 学生的解决方案测试员。它从CLI获取另外两个可执行文件的路径,第一个是学生的测试程序,第二个是标准正常工作的程序,它解决了同样的问题。
学生程序的输入/输出是严格规定的,因此测试人员运行两个程序并比较其输出的大量随机输入,所有不匹配将被报告。
输入/输出最大尺寸估计为几百千字节
示例:..实现插入排序算法...第一行有序列长度...第二行有数字序列a_i其中| a_i | < 2 ^ 31 - 1 ...... 输出第一行必须是所有元素的总和,第二行必须是排序顺序。
输入:
5
1 3 4 6 2
预期产出:
16
1 2 3 4 6
答案 0 :(得分:4)
阅读Advanced Linux Programming - 至少有一整章来回答您的问题 - 并详细了解execve(2),fork(2),waitpid(2),pipe(2), dup2(2),poll(2) ...
请注意,您需要(至少在单线程程序中)对程序的输入和输出进行多路复用(使用poll
)。否则您可能会遇到死锁:子进程可能被阻止写入您的程序(因为输出管道已满),并且您的程序可能被阻止从中读取(因为输入管道为空)。
顺便说一句,如果你的程序有一些event loop它可能会有所帮助(实际上poll
正在为简单的事件循环提供基础)。 Glib(来自GTK)为spawn processes提供功能,Qt有QProcess,libevent知道它们等等。
答案 1 :(得分:2)
鉴于处理只是一个父母与孩子之间的消息问题(必须在孩子回应之前完成),以及从孩子到父母之间的一条消息,那么很容易处理:
这使得儿童进程像僵尸一样躺在身边。如果父母要多次这样做,或者只是需要知道孩子的退出状态,那么在关闭阅读管道之后,它将等待孩子死亡,收集其状态。
所有这些都是直截了当的常规编码。我相信你可以在SO上找到例子。
由于Stack Overflow显然没有合适的例子,所以这里是上面概述的代码的简单实现。有两个源文件,basic_pipe.c
用于基本管道工作,myprogram.c
应该响应问题中显示的提示。第一个几乎是一般目的;它可能应该循环读取操作(但在我测试它的机器上没有关系,这是运行Ubuntu 14.04衍生物)。第二个非常专业。
basic_pipe.c
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
static char msg_for_child[] = "calculate 2 + 2\nsay 'hello world!'\n";
static char cmd_for_child[] = "./myprogram";
static void err_syserr(const char *fmt, ...);
static void be_childish(int to_child[2], int fr_child[2]);
static void be_parental(int to_child[2], int fr_child[2], int pid);
int main(void)
{
int to_child[2];
int fr_child[2];
if (pipe(to_child) != 0 || pipe(fr_child) != 0)
err_syserr("Failed to open pipes\n");
assert(to_child[0] > STDERR_FILENO && to_child[1] > STDERR_FILENO &&
fr_child[0] > STDERR_FILENO && fr_child[1] > STDERR_FILENO);
int pid;
if ((pid = fork()) < 0)
err_syserr("Failed to fork\n");
if (pid == 0)
be_childish(to_child, fr_child);
else
be_parental(to_child, fr_child, pid);
printf("Process %d continues and exits\n", (int)getpid());
return 0;
}
static void be_childish(int to_child[2], int fr_child[2])
{
printf("Child PID: %d\n", (int)getpid());
fflush(0);
if (dup2(to_child[0], STDIN_FILENO) < 0 ||
dup2(fr_child[1], STDOUT_FILENO) < 0)
err_syserr("Failed to set standard I/O in child\n");
close(to_child[0]);
close(to_child[1]);
close(fr_child[0]);
close(fr_child[1]);
char *args[] = { cmd_for_child, 0 };
execv(args[0], args);
err_syserr("Failed to execute %s", args[0]);
/* NOTREACHED */
}
static void be_parental(int to_child[2], int fr_child[2], int pid)
{
printf("Parent PID: %d\n", (int)getpid());
close(to_child[0]);
close(fr_child[1]);
int o_len = sizeof(msg_for_child) - 1; // Don't send null byte
if (write(to_child[1], msg_for_child, o_len) != o_len)
err_syserr("Failed to write complete message to child\n");
close(to_child[1]);
char buffer[4096];
int nbytes;
if ((nbytes = read(fr_child[0], buffer, sizeof(buffer))) <= 0)
err_syserr("Failed to read message from child\n");
close(fr_child[0]);
printf("Read: [[%.*s]]\n", nbytes, buffer);
int corpse;
int status;
while ((corpse = waitpid(pid, &status, 0)) != pid && corpse != -1)
err_syserr("Got pid %d (status 0x%.4X) instead of pid %d\n",
corpse, status, pid);
printf("PID %d exited with status 0x%.4X\n", pid, status);
}
static void err_syserr(const char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
exit(EXIT_FAILURE);
}
#include <stdio.h>
int main(void)
{
char buffer[4096];
char *response[] =
{
"4",
"hello world!",
};
enum { N_RESPONSES = sizeof(response)/sizeof(response[0]) };
for (int line = 0; fgets(buffer, sizeof(buffer), stdin) != 0; line++)
{
fprintf(stderr, "Read line %d: %s", line + 1, buffer);
if (line < N_RESPONSES)
{
printf("%s\n", response[line]);
fprintf(stderr, "Sent line %d: %s\n", line + 1, response[line]);
}
}
fprintf(stderr, "All done\n");
return 0;
}
请注意,无法保证孩子在父母开始执行be_parental()
功能之前完成。
Child PID: 19538
Read line 1: calculate 2 + 2
Sent line 1: 4
Read line 2: say 'hello world!'
Sent line 2: hello world!
All done
Parent PID: 19536
Read: [[4
hello world!
]]
PID 19538 exited with status 0x0000
Process 19536 continues and exits
答案 2 :(得分:0)
您可以使用expect来实现此目的: http://en.wikipedia.org/wiki/Expect
这是通常期望的程序会做的事情:
# Start the program
spawn <your_program>
# Send data to the program
send "calculate 2 + 2"
# Capture the output
set results $expect_out(buffer)
Expect可以在使用expect开发库的C程序中使用,因此您可以将以前的命令直接转换为C函数调用。这里有一个例子:
http://kahimyang.info/kauswagan/code-blogs/1358/using-expect-script-cc-library-to-manage-linux-hosts
你也可以在perl和python中使用它,它通常比C更容易为这些目的编程。