I have been stuck on this for some time.
Let’s say I have a C program like the following. I want to be able to send this program some string and get the control after that.
If I do:
–> cat myfile | myprogram
or
–> echo “0123” | myprogram
or
–> myprogram < myfile
I get the ouput (myfile contains “0123”)
30 31 32 33
Using the -n option raises a segfault
–> echo -n mystring | ./test
zsh: done echo -n “0123” |
zsh: segmentation fault ./test
I also tried with a named pipe, but it didn’t work either.
I would like to be able to do something like
cat myfile | myprogram
and get back the control so that I can type other characters.
1 #include <stdlib.h>
2 #include <stdio.h>
3
4 int main (int argc, char *argv[]) {
6 int i = 0, j;
7 unsigned char buf[512];
8 unsigned char x;
9
10 while ((x = getchar()) != '\n') {
11 buf[i] = x;
12 i++;
13 }
14
16 for (j = 0; j < i; j++) {
17 printf("%x ", buf[j]);
18 }
19 printf ( "\n" );
20
21 return EXIT_SUCCESS;
22 } // end of function main
EDIT:
Below is the wrapper I have come up with.
It does everything I want, except that the output of the child exec-ed file is not properly displayed.
Without the wrapper:
$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
2+2
4
With the wrapper:
$ ./wrapper bc
2+2
enter
4
Deleting the line
dup2(pipefd[0], 0); // Set the read end of the pipe as stdin.
makes the child stdout display correctly, but of course breaks the wrapper.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <assert.h>
int main(int argc, char const *argv[]) {
int cpid;
int pipefd[2];
if (pipe(pipefd) == -1) { perror("pipe.\n"); exit(errno); }
cpid = fork();
if (cpid == -1) { perror("fork."); exit(errno); }
if (cpid) {
// Parent --------------------------------------------------------
int buf_size = 8192;
char buf[buf_size];
size_t file;
// Close the unused read end of the pipe.
close(pipefd[0]);
// Leave a bit of time to the child to display its initial input.
sleep(2);
while (1) {
gets(buf);
if (strcmp("enter", buf) == 0) {
write(pipefd[1], "\n", 1);
} else if (-1 != (file = open(buf, O_RDONLY))) {
// Dump the output of the file to the child's stdin.
char c;
while(read(file, &c, 1) != 0) {
switch(c) {
case '\n':
printf("(skipped \\n)");
break;
default:
printf("%c", c);
write(pipefd[1], &c, 1);
};
}
printf("\n");
} else {
// Dump input to the child's stdin, without trailing '\n'.
for (int i = 0; (buf[i] != 0); i++) {
write(pipefd[1], buf + i, 1);
}
}
}
// Wait for the child to exit.
printf("Waiting for child to exit.\n");
wait(NULL);
} else {
// Child ---------------------------------------------------------
// Close the unused write end of the pipe.
close(pipefd[1]);
// Set the read end of the pipe as stdin.
dup2(pipefd[0], 0); // Set the read end of the pipe as stdin.
char** program_arguments = (char**)(argv + 1);
if (execvp(argv[1], program_arguments) < 0) {
perror("execvp.\n");
exit(errno);
}
}
}
I do not think it is possible to achieve this using named pipes if you can not modify the behavior of the program. Since in essence named pipes are no different then giving the output from standard input with redirection.
I also do not think it is possible if you use pipe or redirection properties of the shell, since always an EOF is sent to your program in this case and you can not ignore EOF since you can not modify the program.
A possible solution is to use a wrapper. The wrapper will first read the prepared input, send them to your program, after the prepared input finishes the wrapper switches to standard input. Actual program just keeps consuming input, it is not aware of the actual source of the data.
Only drawback is, you can not provide prepared input with pipes or redirection, you have to supply a filename. (I’m not sure a named pipe will work or not.) The reason is obvious, if you provide the prepared input to wrapper from standard input then the same problem exists for wrapper. By this way you are just delegating the problem to wrapper, which you can design any way you want.
A possible implementation in C (modified from a similar wrapper I’ve used, not tested extensively):
You can run your program with this wrapper as :
You can put your initial input into
input.txt.A simpler solution is to reopen the standard input. However if you simply try to open it as if you are opening a file, it does not work. You should open the terminal stream and copy it to standard input of your application. You can do it (again by using a wrapper) with something like:
Not to mention this second solution is for Linux and not portable.