您当前的位置:首页 > 计算机 > 系统应用 > Linux

小议 bash 中的 COPROC

时间:12-14来源:作者:点击数:
城东书院 www.cdsy.xyz

在 bash 中,两个进程之间要传输数据的最常见方法就是用匿名管道了,像这样

commandA|commandB|commandC

但是使用匿名管道最大的一个问题是,数据的传输是单向的,即 commandB 只能从 commandA 的输出中读取数据,然后 commandB 的输出只能被 commandC 读取。

那么若想要两个进程之间相互读取数据怎么办呢?也就是说我希望上面的 commandA 和commandC 其实是同一个进程怎么办? 一个常见的解决方法就是使用命名管道:

mkfifo in out
trap 'rm in out' EXIT
cmd <in >out &
exec 3> in 4< out
echo $data >&3
read $data <&4

不过使用命名管道也有一个麻烦事,就是需要记得清理生成的管道文件。

不过,自 bash 4.0 开始,bash 引入了一个关键字 coproc, 用来在后台创建一个协作进程(co-process),同时将它的输入和输出通过管道与文件句柄相连。

有点类似于C中的 FILE * popen ( const char * command , const char * type ); 函数,只不过它同时产生输入和输出两个句柄。

COPROC 语法

根据 bash manual 的说法,coprocess 的语法为

coproc [NAME] command [redirections]

其中当 command 为简单命令( gnu 组织网/software/bash/manual/html_node/Simple-Commands.html)时,不能有 NAME

这个说法很容易让人感到困惑,什么叫简单命令?这个概念不直观。

直观点说,可以认为 coproc 有两类用法,一类是基本语法,一类是扩展的语法。

COPROC 基本语法

COPROC 的基本语法适用于当只需要有一个 coprocess 的情况,它的语法为

coproc cmd [redirections]

这时与 coprocess 的输入/输出管道相连的句柄保存在 $COPROC 数组中,分别为 ${COPROC[1]} 和 ${COPROC[0]}

因此,你可以使用 echo $data >&"${COPROC[1]}" 来往 coprocess 中输入数据, 通过使用 echo $data <&"${COPROC[0]}" 来读取 coprocess 的输出数据。

COPROC 的扩展语法

当需要创建多个 coprocess 时,你就需要使用 coproc 的扩展语法了,因为它允许你为 coprocess 命名。

coproc NAME {cmds} [redirections]

有没有觉得它跟 bash 中定义函数的语法 function NAME {cmds} 很类似?

这时与 coprocess 的输入/输出管道相连的句柄保存在 $NAME 数组中,分别为 ${NAME[1]} 和 ${NAME[0]}

对应的,你可以使用 echo $data >&"${NAME[1]}" 来往 coprocess 中输入数据,通过使用 echo $data <&"${NAME[0]}" 来读取 coprocess 的输出数据。

关闭不需要的管道

若coprocess只需要输入句柄或输出句柄,则可以使用 exec 来关闭不需要的文件句柄

exec {NAME[0]}>&-
exec {NAME[1]}>&-

注意事项

使用 coprocess 虽然方便,但要当心 coprocess 由于输出缓存而导致的卡死。

比如下面这个例子

coproc tr a b
echo a >&"${COPROC[1]}"
read var<&"${COPROC[0]}"

你的期望是第三句能够读出字符 b 作为 var 的值, 然而实际上执行到第三句话时会卡死。 这是因为 tr 命令缓存了输出的内容,而并未将其写到终端上来。

因此创建 coprocess 时一定小心,只能使用那些不会缓存输出的命令。 也正因为此,coprocess 的使用范围其实也很受限,真要用来跟其他进程做交互的话,还是推荐使用 expert 比较好。

城东书院 www.cdsy.xyz
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐