我想在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