Reputation: 3818
Suppose I have a ncurses program which does some job on curses screen, and finally print something to stdout
. Call this program c.c
, compiled to a.out
.
I expect cat $(./a.out)
first fire up ncurses, after some action, a.out
quits and print c.c
to stdout
, which is read by cat
, and thus print content of file c.c
.
#include <stdio.h>
#include <ncurses.h>
int main() {
initscr();
noecho();
cbreak();
printw("hello world");
refresh();
getch();
endwin();
fprintf(stdout, "c.c");
return 0;
}
I also expect ./a.out | xargs vim
, ls | ./a.out | xargs less
to work.
But when I type ./a.out | xargs vim
, hello world
never shows up. The command seems not executed in order, vim does not open c.c
.
What is the correct way to make a ncurses program to work with other linux utils?
Upvotes: 1
Views: 745
Reputation: 123410
ncurses
works by writing a bunch of ansi escapes to stdout, which the terminal will interpret. You can run ./a.out > file
and then inspect the file to see what you're actually writing. It'll be immediately obvious why programs are confused:
$ cat -vE file
^[(B^[)0^[[?1049h^[[1;24r^[[m^O^[[4l^[[H^[[Jhello world^[[24;1H^[[?1049l^M^[[?1l^[>c.c
The correct way of doing this is to skip all the graphical/textual UI parts when you detect that stdout is not a terminal, i.e. it's consumed by a program instead of a user:
#include <unistd.h>
#include <stdio.h>
#include <ncurses.h>
int main() {
if(isatty(1)) {
// Output is a terminal. Show stuff to the user.
initscr();
noecho();
cbreak();
printw("hello world");
refresh();
getch();
endwin();
} else {
// Output is consumed by a program.
// Skip UI.
}
fprintf(stdout, "c.c");
return 0;
}
This is the canonical Unix behavior.
If you instead want to force your UI to be shown regardless, you can draw your UI on stderr.
Upvotes: 1
Reputation: 54475
Pipes use the standard output (stdout) and standard input (stdin).
The simplest way - rather than using initscr
, which initializes the output to use the standard output, use newterm
, which allows you to choose the file descriptors, e.g.,
newterm(NULL, stderr, stdin);
rather than
initscr();
which is (almost) the same as
newterm(NULL, stdout, stdin);
By the way, when you include <ncurses.h>
(or <curses.h>
), there is no need to include <stdio.h>
.
If you wanted to use your program in the middle of a pipe, that is more complicated: you would have to drain the standard input and open the actual terminal device. But that's another question (and has already been answered).
Further reading:
Upvotes: 2