Команда 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>
, поэтому все стандартные перенаправления оболочки работают. Таким образом, вы используете круглые скобки, чтобы получить именно то, что вам нужно, - чтобы сама диалоговая программа отправляла свой вывод на управляющий терминал, а ее stderr перенаправлялся туда, где вы можете прочитать его с помощью popen.
Существует еще один метод, который имеет те же недостатки, что и решение popen
, и имеет дополнительный недостаток, заключающийся в необходимости открывать и читать другой файл после завершения диалога. Но у этого есть преимущество простоты. К сожалению, это также требует от вас возможности записи в файловую систему, и наиболее естественное место для этого (/ tmp) чревато проблемами безопасности, связанными с тем, чтобы кто-то еще не каким-то образом не взломал ваш файл. Этот метод должен сделать system("dialog --menu plus other arguments 2>temp_file");
. Затем вы читаете из временного_файла, когда это будет сделано.
Они оба немного уродливы, тем более что в диалоге много аргументов, которые могут содержать метасимволы оболочки. Поэтому, даже если вышеуказанное сработает, я настоятельно рекомендую использовать комбинацию 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;
}