我想从我的C程序中显示Linux命令dialog --menu
的输出,以便用户可以从菜单中选择一个选项。此外,程序的最后一行输出是用户选择的选项,因此我需要捕获它。
我尝试使用popen()
和system()
来完成此操作,并在网上查看,但找不到任何可以带来成功结果的内容。
如果我找不到使用dialog
的方法,我将不得不使用更简单的方法(简单的“输入你的选择并按回车”),它就不会那么酷。< / p>
提前谢谢你, 布赖恩
答案 0 :(得分:3)
dialog
命令打印用户在stderr上的选择结果。这意味着你必须捕获stderr,而不是stdout。这有点棘手。我将自己去测试一下,但我的猜测是最简单的方法是使用popen
这样:
FILE *dialog = popen("(dialog --menu plus other arguments >/dev/tty) 2>&1");
然后你可以从dialog
文件中读取(如果它当然不是NULL)。
这是有效的,因为popen的参数实际上是传递给sh
的调用。这意味着popen确实运行sh -c <argument of popen>
所以所有标准shell重定向都有效。因此,您可以使用括号来获得您想要的内容,这是对话框程序本身将其输出发送到其控制终端,并将其stderr重定向到您可以使用popen读取它的位置。
还有另一种方法与popen
解决方案具有相同的缺点,并且还有一个缺点是需要在对话框完成后打开并读取另一个文件。但它具有简单的优点。不幸的是,它还要求您能够写入文件系统,而最自然的地方(/ tmp)充满了与确保其他人不以某种方式劫持您的文件相关的安全问题。该方法是system("dialog --menu plus other arguments 2>temp_file");
。然后在完成后从temp_file中读取。
这些都有点难看,特别是因为对话框需要很多可能在其中包含shell元字符的参数。因此,即使进行了上述工作,我强烈建议您使用pipe
,fork
,execvp
,fdopen
和waitpid
的组合来获得结果'之后。
这样的骨架看起来像这样:
#include <stdio.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
int main(int argc, const char *argv[])
{
const char *dia_args[] = {
"dialog",
"--output-fd",
NULL,
"--menu",
"Hi there",
"60", "15", "15",
"t1", "i1",
"t2", "i2",
"t3", "i3",
"t4", "i4",
"t5", "i5",
NULL
};
int pipefds[2];
if (pipe(pipefds) < 0) {
perror("pipe failed");
return 1;
} else {
const pid_t child = fork();
if (child < 0) {
perror("fork failed");
return 1;
} else if (child == 0) {
char pipefdstr[60];
close(pipefds[0]);
if (snprintf(pipefdstr, sizeof(pipefdstr) - 1, "%u", pipefds[1]) < 0) {
perror("snprintf failed");
return 1;
} else {
pipefdstr[sizeof(pipefdstr) - 1] = '\0'; /* Protect against bugs in snprintf */
dia_args[2] = pipefdstr;
execvp(dia_args[0], dia_args);
perror("Unable to exec dialog command");
return 1;
}
} else { /* child > 0 */
FILE *dialog = fdopen(pipefds[0], "r");
char inbuf[200];
int waitresult = 0;
if (dialog == NULL) {
perror("Unable to fdopen");
kill(child, SIGKILL);
return 1;
}
close(pipefds[1]);
while (fgets(inbuf, sizeof(inbuf) - 1, dialog)) {
inbuf[sizeof(inbuf) - 1] = '\0';
printf("Got [%s] from dialog.\n", inbuf);
}
fclose(dialog);
if (waitpid(child, &waitresult, 0) < 0) {
perror("waitpid failed");
return 1;
}
if (!WIFEXITED(waitresult) || (WEXITSTATUS(waitresult) != 0)) {
fprintf(stderr, "dialog exited abnormally.");
return 1;
}
}
}
return 0;
}
答案 1 :(得分:1)
fp = fopen(SCRIPT_FILE, "w+");
fprintf(fp,
"#!/bin/sh\\n\\n"
"dialog --clear --title \""BRAND_INFO"\""
" --inputbox \""TITLE"\\n\\n"
"Please input the name[/tmp/input]:\" "
BOX_HEIGHT" "BOX_WIDTH" 2> "RESULT_FILE"\n");
fclose(fp);
system("bash "SCRIPT_FILE);
fp = fopen(RESULT_FILE, "rt");
// here read the results from the RESULT_FILE
// into which dialog had written the results.
...
有点无聊加上一点性能损失,但对于清单和菜单等对话框的所有组成部分都是可行的。
您可以控制将存储在SCRIPT_FILE中的脚本的所有详细信息,并且整个操作和流控制很简单。
答案 2 :(得分:0)
我确定我要求批评的风暴,但这是基本的想法。我没有检查这个编译器错误,也没有提供头文件。这只是我在喝酒时掀起的代码片段。
基本上,您fork()
在子进程中重定向 stderr 并调用exec()
,在父进程中调用waitpid()
并获取返回值“对话框,“如果没有错误,请阅读您重定向的文件 stderr 。
pid_t pid;
char cmd[] = "dialog";
char *args[] = {"dialog", "--menu", NULL};
int status;
int fd;
if((pid = fork()) == 0)
{
/* child */
/* I believe dialog prints to stderr the answer chosen
* also you should check the return value of open()
*/
fd = open("some_file_name", O_WRONLY | O_CREAT | O_TRUNC, 0);
close(STDERR_FILENO);
dup(fd);
execvp(cmd, args);
perror("execvp()");
exit(1);
}
else if(pid < 0)
{
perror("fork()");
exit(1);
}
else
{
/* parent */
/* you should also check the return of waitpid()
* this is just for example
*/
waitpid(pid, &status, 0);
/* if everything was ok then status has the return value
* also the file "some_file_name" should have the output
*/
}
答案 3 :(得分:0)
您可以使用管道获取标准输出并提供子应用程序输入(由主应用程序控制)。您需要fork
并使用传统的exec
而不是popen或系统。