This question has been asked for many languages but I have yet to find the bash-flavored duplicate.
Suppose I have a program that alternates between writing stdout
and reading stdin
.
#include <stdio.h>
/*
* Control_D to exit.
*/
int main(int argc, char** argv){
char init = 'C';
if (argc > 1) {
init = *argv[1];
}
putchar(init);
putchar('\n');
while (1) {
int c = getchar();
if (c == -1) {
return 0;
}
putchar(c);
putchar('\n');
}
}
I would like to write a bash script that reads what the program wrote and then decides what to write standard input, and does this repeatedly. That is, something like this:
myProgram &
for i in $(seq 1 10);
do
output=$(# magic command to read myProgram stdout)
if [[ $output = "C" ]]; then
# Magic command to write 'C' to myProgram input
fi
if [[ $output = "D" ]]; then
# Magic command to write 'E' to myProgram input
done
I initially tried to do this with named pipes but this did not work because pipes require both ends to be open before starting and using various exec
tricks did not manage to workaround these limitations. I am not ruling them out as a solution, merely pointing out that I was not sufficiently clever to get them to work.
Do these magic commands exist in bash, or do I have to switch to another language?
Let's assume for the sake of this question that I have no control over myProgram
and cannot dictate how it communicates; it only understands stdin and stdout because it was intended to be used interactively by a user.
I think you are looking for the coproc
builtin. It allows you run commands asynchronously and provides you file descriptors to interact with i.e. a pair of fd's, connected to both stdin and stdout of the command
coproc myProgram
The built-in returns the fd pair in the array named COPROC
if no name is provided by default. You need something like
To write to the program
printf 'foo' >&${COPROC[1]}
To read from the program
read -u "${COPROC[0]}" var
So your whole program would look like below. Assuming myprogram
is the executable available in the current path.
coproc ./myProgram
for ((i=1; i<=10; i++)); do
read -u "${COPROC[0]}" var
if [[ $var = "C" ]]; then
printf 'C' >&${COPROC[1]}
elif [[ $var = "D" ]]; then
printf 'E' >&${COPROC[1]}
fi
done
Like running a background job using &
provides the process id in $!
running the program using coproc
automatically updates the process id in COPROC_PID
variable, so that you can do below, when you are done with the program
kill "$COPROC_PID"
Untested, but I think you might need to flush out stdout as its not line buffered by default. Use fflush(stdout)
from your C program or run the executable with stdbuf -oL