我想在C程序中显示Linux命令dialog --menu
的输出,以便用户可以从菜单中选择一个选项。另外,程序输出的最后一行是用户选择的选项,所以我需要捕获它。运行带有用户输入的外部命令C
我试过使用popen()
和system()
来实现这一点,并在网上查找,但找不到任何能够导致成功结果的东西。
如果我找不到使用dialog
的方法,我将不得不使用一种更简单的方法(一种简单的“输入您的选择并按回车”),它不会很酷。
谢谢你在前进, 布赖恩
我想在C程序中显示Linux命令dialog --menu
的输出,以便用户可以从菜单中选择一个选项。另外,程序输出的最后一行是用户选择的选项,所以我需要捕获它。运行带有用户输入的外部命令C
我试过使用popen()
和system()
来实现这一点,并在网上查找,但找不到任何能够导致成功结果的东西。
如果我找不到使用dialog
的方法,我将不得不使用一种更简单的方法(一种简单的“输入您的选择并按回车”),它不会很酷。
谢谢你在前进, 布赖恩
dialog
命令在stderr上打印用户选择的结果。这意味着你将不得不捕获标准错误,而不是标准输出。这有点棘手。我要去这个测试自己,但我的猜测是最容易做的事情是使用popen
这样的:
FILE *dialog = popen("(dialog --menu plus other arguments >/dev/tty) 2>&1");
然后你可以从dialog
文件读取(只要它不为空,当然)。
这是可行的,因为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;
}
我敢肯定,我要求批评了轩然大波但尽管如此这里的基本思想。我没有检查编译器错误,也没有提供头文件。这只是我在喝酒的时候掀起的一段代码片段。
基本上,你fork()
,子进程中的重定向标准错误,并呼吁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
*/
}
一个合理简单的解决方案。一个可能的增强将是'从文件系统中'unlink()'fd,以便只有孩子和父母可以访问它,并且使用'mkstemp()来随机化文件名 – 2009-10-29 10:31:44
您可以使用管道获取标准输出并给出子应用程序输入(由主应用程序控制)。但是,您需要fork
并使用常规exec
而不是popen或系统。
好,我这样做了相当不错。请参见下面的代码示例:
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.
...
有点沉闷再加上一点点perfromance损失,但是对于可行,如清单和菜单等对话的所有组件
你可以控制这将是脚本的所有细节存储在SCRIPT_FILE中,整个操作和流程控制很简单。
我使用了第一个选项,它工作得很好。 非常感谢。 – HalfBrian 2009-10-25 17:25:22
使用'fgets(inbuf,sizeof(inbuf),对话框)'not'sizeof(inbuf) - 1'。阅读fgets的手册 – user102008 2010-12-22 00:24:42
@ user102008 - 即使我知道它可能是'sizeof(inbuf)',我仍然会使用'sizeof(inbuf) - 1'来防止'fgets'中的实现错误。 – Omnifarious 2010-12-22 15:42:36