You are not logged in.
I am trying to write output to "less" from a C program to allow paging. I have tried using popen (3), similar as in the following simple test program:
#include <stdio.h>
int main(int argc, char **argv)
{
FILE *out;
int i;
out= popen("/usr/bin/less", "w");
for( i= 0; i< 512; ++i )
fprintf(out, "This is line %d\n", i);
pclose(out);
}
This mostly works, but behaves differently from other programs that do the same thing. In particular, you cannot use Ctrl-C to stop skipping to the end of the output without killing both less and the original program. (Apparently less normally catches the signal, but is prevented from doing so when I start it as above.)
Both when typing "| less" in the shell and when using "man", this works. I have looked at the output of pstree and noticed the following differences: When piping into less from the shell, the less process is a child of the shell, so this is clearly different from what I can get with popen(). But when man is running, the less process is also a child of man, but it still behaves correctly.
Has anyone done this kind of thing before or has any ideas?
Officer, I had to drive home - I was way too drunk to teleport!
Offline
You want to play with pipe/fork:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
void work(void) {
int i;
for (i=0; i<512; i++) {
printf("Line %d\n", i);
}
}
int main(void) {
int pfds[2];
pid_t pid;
if (pipe(pfds) < 0) {
perror("pipe failed");
return EXIT_FAILURE;
}
pid = fork();
if (pid < 0) {
perror("fork failed");
return EXIT_FAILURE;
} else if (pid == 0) { /* child */
close(pfds[0]); /* close unused read end */
/* set write end of pipe as stdout for this child process */
dup2(pfds[1], STDOUT_FILENO);
close(pfds[1]);
work();
} else /* if (pid > 0) */ { /* parent */
char *args[] = { "less", NULL };
close(pfds[1]); /* close unused write end */
/* set read end of pipe as stdin for this process */
dup2(pfds[0], STDIN_FILENO);
close(pfds[0]); /* already redirected to stdin */
execvp(args[0], args);
perror("exec failed");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
Hint for the future, you can use strace to observe how programs work. For pipes:
strace -o log.txt -f sh -c 'echo 1337 | less'
(`-o log.txt` writes to that file, `-f` takes forks into account)
Offline
If I understand you correctly, I will not get what I want using popen(). Pity, it would have been a nice shortcut. I can see that in your code, the parent process exec's "less" and the child process generates the text, which is the opposite of how popen is documented to work. I will try your version out soon, thank you! And thanks for reminding me of strace.
Edit: Lekensteyn's solution worked as I wanted, and behaves like a shell pipe. Thanks again for the instructive example program. The man program uses a library, libpipeline, that allows to do more complex stuff such as constructing a pipe (like the shell) and feeding output to multiple programs.
Last edited by deepsoul (2013-03-14 12:10:10)
Officer, I had to drive home - I was way too drunk to teleport!
Offline