繁体   English   中英

从管道的后期终止管道中的特定进程

[英]Killing a specific process in a pipeline, from later in the pipeline

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

#!/bin/sh

SERVER_ADD=localhost
SERVER_PORT=9995

result=$( ( echo '{"op":"get","path":"access"}'; sleep 30 ) | telnet "$SERVER_ADD" "$SERVER_PORT" )
    
echo "$result"

这是可行的,只是sleep时间必须长于服务器响应所需的最长时间。 这意味着每次调用都需要很长时间,这将最小时间从几十毫秒推到了几十秒。 我需要我的脚本等待第一行响应,然后终止进程,以便它可以 go 继续执行其他操作。

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

经过多次试验和错误,我想出了以下内容:

#!/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,然后休眠足够长的时间以获得最长的响应时间
  • Pipe 到telnet建立连接
  • Pipe 服务器响应一个循环,一次一行
  • 捕获并回显第一行后,按名称终止sleep命令,这将结束子shell,从而结束 telnet 连接
  • 2>/dev/null使用sleep命令在终止时打印的“终止”消息

这很好用,除了那个killall sleep 这将杀死该用户控制下的每个sleep实例。 大多数时候这不会是一个问题,但其他时候这将是一个严重混乱的错误来源。

我怎样才能在需要时杀死sleep (或整个子外壳),而不会造成附带损害?

如果您有bash您可以尝试使用协同处理:

coproc telnet "$SERVER_ADD" "$SERVER_PORT" 
echo '{"op":"get","path":"access"}' >&${COPROC[1]}
result=
while IFS= read -r line
do    result+="$line
"
done <&${COPROC[0]}

coproc bash 命令启动一个进程,并创建一个名为COPROC的数组,其中包含两个文件描述符,即进程的标准输入和标准输出。 然后,您可以随意书写和阅读它们。 如果您的 telnet 在一个命令后没有结束,您可能需要发送第二个命令(例如echo 'exit' >&${COPROC[1]} )来断开连接。

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

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

通过只打开一次锁定文件,并为其分配文件描述符 5(任意), flock 5将锁定它, flock -u 5将解锁它。

result=$(
    exec 5>mylock
    flock 5
    ( echo '{"op":"get","path":"access"}'; flock mylock true ) |
    telnet "$SERVER_ADD" "$SERVER_PORT" |
    while read line; do
        echo "$line"
        flock -u 5
    done 
) 2>/dev/null

请参阅此其他问题以获取对flock -u的简单测试。

您可以尝试使用 fifo 将数据发送到 telnet,这样您就可以在需要的时间关闭它。

rm -f myfifo
mkfifo myfifo
result=$(
    telnet "$SERVER_ADD" "$SERVER_PORT"  <myfifo  |
    (  echo '{"op":"get","path":"access"}'  >&5
       while read -r line; do
          echo "$line"
          exec 5>&-
       done
    ) 5>myfifo
)

语法5>myfifo (无空格)打开一个新的 output 文件描述号 5 写入 fifo。 echo >&5写入这个 fd。 exec 5>&-关闭这个 fd。 这种语法应该在ash中工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM