Shell 终止管道中的特定进程,从管道的后面开始

Shell 终止管道中的特定进程,从管道的后面开始,shell,process,telnet,Shell,Process,Telnet,我正在编写一个shell脚本,它使用telnet连接到服务器,发送查询,然后获取单行响应以进行进一步处理。经典的解决方案是设置一个子shell,echos查询,然后运行sleep,以保持连接打开足够长的时间,以便返回响应: #!/bin/sh SERVER_ADD=localhost SERVER_PORT=9995 result=$( ( echo '{"op":"get","path":"access"}';

我正在编写一个shell脚本,它使用
telnet
连接到服务器,发送查询,然后获取单行响应以进行进一步处理。经典的解决方案是设置一个子shell,
echo
s查询,然后运行
sleep
,以保持连接打开足够长的时间,以便返回响应:

#!/bin/sh

SERVER_ADD=localhost
SERVER_PORT=9995

result=$( ( echo '{"op":"get","path":"access"}'; sleep 30 ) | telnet "$SERVER_ADD" "$SERVER_PORT" )
    
echo "$result"
这是可行的,只是
睡眠时间必须比服务器响应所需的最长时间长。这意味着每次调用都需要很长的时间,这将最短时间从几十毫秒推到几十秒。我需要我的脚本等待第一行响应,然后终止进程,以便继续执行其他操作

(是的:我知道显而易见的答案是“使用”。不幸的是,我的目标是一个嵌入式系统,添加
expect
和脚本引擎将使我的图像大小增加大约1兆字节。不行。)

经过反复试验,我得出以下结论:

#!/bin/sh

SERVER_ADD=localhost
SERVER_PORT=9995

result=$( ( echo '{"op":"get","path":"access"}'; sleep 30 ) |
    telnet "$SERVER_ADD" "$SERVER_PORT" |
    while read -r line; do
        echo "$line"
        killall sleep
    done ) 2>/dev/null
echo "$result"
解释

  • 设置一个响应查询的子shell,然后休眠足够长的时间以获得最长的响应时间
  • 通过管道连接到telnet进行连接
  • 通过管道将服务器响应传递到循环,每次一行
  • 捕获并回显第一行后,按名称终止
    sleep
    命令,该命令将结束子shell,从而结束telnet连接
  • 2>/dev/null
    使用
    sleep
    命令终止时打印的“Terminated”消息
这很有效,除了
停止所有睡眠
。这将杀死此用户控制下的每个
sleep
。大多数情况下,这不会是一个问题,但其他情况下,这将是一个严重的错误来源


我如何在需要时杀死该
睡眠
(或整个子shell),而不造成附带损害?

如果您有
bash
,您可以尝试使用协进程:

coproc telnet“$SERVER\u ADD”“$SERVER\u PORT”
echo“{”op:“get”,“path:“access”}>&${COPROC[1]}
结果=
而IFS=读取-r行
执行结果+=“$行
"

完成您可以尝试在运行telnet时,在任意文件上使用
flock
命令替换睡眠。第二个
flock
代替睡眠将挂起。第三个
flock-u
可以随时释放锁

虽然典型的用法是
flock lockfile命令
,但此脚本利用了
flock filedescriptor
的用法。这是因为
flock-u
只能在文件描述符上使用,并且没有命令(添加命令会强制将文件描述符arg作为文件名)

只要打开锁定文件一次,并将其指定为文件描述符5(任意),
flock 5
将锁定它,而
flock-u 5
将解锁它

结果=$(
exec 5>mylock
羊群5
(回显“{”op:“get”,“path:“access”}”;flock mylock true)|
telnet“$SERVER\u ADD”“$SERVER\u PORT”|
边读边做
回音“$line”
鸥群-u 5
完成
)2>/dev/null

查看另一个,了解对flock-u的简单测试。您可以尝试使用fifo将数据发送到telnet,以便在需要的时间关闭它

rm-f myfifo
mkfifomyfifo
结果=$(
telnet“$SERVER\u ADD”“$SERVER\u PORT”&5
当读取-r行时;执行
回音“$line”
执行官5>&-
完成
)5>我的FIFO
)

语法
5>myfifo
(无空格)打开写入fifo的新输出文件描述编号5。
echo>&5
写入此fd。
exec 5>&-
关闭此fd。这种语法应该适用于
ash

非常酷;我以前没看过《科普洛克》。不幸的是,我没有bash(它是busybox版本的ash),也没有coproc。我不能让它为我工作。仍在努力,但任何提示都会有所帮助。(Yowza:每个答案都教会我一些东西。谢谢。)我把
放错地方了,对不起。Telnet是一个棘手的协议。并非所有服务器在开始读取输入、断开连接或注意到断开连接时的反应都相同。工作正常!太棒了。我还是无法使用这个版本。但是,你对
flock
与文件描述符一起使用的描述将是对我在此之后提出的问题的一个很好的回答;很高兴向上投票/接受它作为一个答案。为了完整性,我重新激活并修改了这个答案。现在我们有一个工作示例,说明如何使用带有
-u
的锁定文件和fd。普里莫:现在也可以了。在你的两个与coproc无关的问题中,你认为哪一个是最好的答案?最好的答案总是最容易写、理解和维护的,所以对我来说fifo是最好的答案。太棒了。谢谢你的帮助。