这个问题已经被问过很多语言,但我还没有找到bash味的重复。
假设我有一个程序,它可以交替地从子进程写入 stdout
和阅读 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');
}
}
我想写一个bash脚本,读取程序所写的内容,然后决定写什么标准输入,并重复这样做。也就是像这样。
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
我最初想用 名管 但这是行不通的,因为管道在启动前需要将两端打开,并使用不同的 exec
技巧无法解决这些限制。我并不是排除它们作为一种解决方案,只是指出我没有足够的智慧让它们工作。
这些神奇的命令在bash中是否存在,还是我必须换一种语言?
为了这个问题,我们假设我无法控制 myProgram
而不能规定它的通信方式;它只理解stdin和stdout,因为它的目的是让用户交互使用。
我想你要找的是 coproc
内置. 它允许你异步运行命令,并为你提供文件描述符来进行交互,例如一对fd,连接到命令的stdin和stdout。
coproc myProgram
内置的返回数组中的fd对,名为 COPROC
如果默认没有提供名称。你需要类似于
要写入程序
printf 'foo' >&${COPROC[1]}
要从程序中读取
read -u "${COPROC[0]}" var
所以你的整个程序会像下面这样。假设 myprogram
是当前路径中可用的可执行文件。
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
就像在后台运行一个工作,使用 &
中提供了流程ID $!
运行程序,使用 coproc
中自动更新进程ID。COPROC_PID
变量,这样你就可以做下面的工作,当你做完程序后
kill "$COPROC_PID"
未经测试,但我认为你可能需要清空stdout,因为默认情况下它没有行缓冲。使用 fflush(stdout)
从你的C程序中,或在运行可执行文件时使用 stdbuf -oL
而作为替代方案的是 coproc
你可以直接使用一个fifo。可以是两个fifo,一个用于输入,一个用于输出,或者是一个fifo和一个带有重定向的文件描述符。下面我使用bash扩展 >(...)
用文件描述符和fifo进行进程替换。
f=/tmp/fifo.fifo
mkfifo "$f"
exec 10> >( { echo "Header!"; sed 's/^/process: /'; } >"$f" )
IFS= read -r first_line <"$f"
echo "First line: $first_line"
# "First line: Header!"
echo 123 >&10
IFS= read -r second_line <"$f"
echo "Second line: $second_line"
# Second line: process: 123
exec 10<&-
rm "$f"
所以,你的程序可以看起来像:
f=/tmp/fifo.fifo
mkfifo "$f"
exec 10> >(myProgram >"$f")
for i in $(seq 1 10); do
IFS= read -r output <"$f"
if [[ $output = "C" ]]; then
echo "C" >&10
fi
if [[ $output = "D" ]]; then
echo "D" >&10
fi
done
exec 10<&-
rm "$f"