从管道的后面杀死管道中的特定进程
Killing a specific process in a pipeline, from later in the pipeline
我正在编写一个 shell 脚本,它使用 telnet
连接到服务器,发送查询,然后获取单行响应以进行进一步处理。经典的解决方案是设置一个 subshell 来 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
必须长于服务器可能响应的最长时间。这意味着每次调用都会花费那么长的时间,这将最短时间从几十毫秒推到几十秒。我需要我的脚本等待第一行响应,然后终止进程,以便它可以继续做其他事情。
(是的:我知道显而易见的答案是“使用 expect
." Unfortunately, I'm targeting an embedded system, and adding expect
and the TcL
编写的脚本引擎会使我的图像大小增加大约 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
命令,这会结束 subshell,从而结束 telnet 连接
2>/dev/null
使用 sleep
命令在终止时打印的“已终止”消息
除了那个 killall sleep
,这很好用。这将杀死该用户控制下的每个 sleep
实例。大多数时候这不会成为问题,但其他时候这将是一个 严重 令人困惑的错误来源。
我怎样才能在需要时杀死那个 sleep
(或整个子shell)而不造成附带伤害?
如果您有 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
的数组,其中包含进程的两个文件描述符,stdin 和 stdout。
然后您可以随意编写和阅读它们。
如果您的 telnet 在一个命令后没有结束,您可能需要发送第二个命令(例如 echo 'exit' >&${COPROC[1]}
)来断开连接。
您可以尝试在任意文件上用 flock
命令替换睡眠,同时 运行 telnet。代替睡眠的第二个 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
(无space)打开一个新的输出文件描述编号5写入fifo。 echo >&5
写入此 fd。 exec 5>&-
关闭这个 fd。此语法应适用于 ash
.
我正在编写一个 shell 脚本,它使用 telnet
连接到服务器,发送查询,然后获取单行响应以进行进一步处理。经典的解决方案是设置一个 subshell 来 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
必须长于服务器可能响应的最长时间。这意味着每次调用都会花费那么长的时间,这将最短时间从几十毫秒推到几十秒。我需要我的脚本等待第一行响应,然后终止进程,以便它可以继续做其他事情。
(是的:我知道显而易见的答案是“使用 expect
." Unfortunately, I'm targeting an embedded system, and adding expect
and the TcL
编写的脚本引擎会使我的图像大小增加大约 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
命令,这会结束 subshell,从而结束 telnet 连接 2>/dev/null
使用sleep
命令在终止时打印的“已终止”消息
除了那个 killall sleep
,这很好用。这将杀死该用户控制下的每个 sleep
实例。大多数时候这不会成为问题,但其他时候这将是一个 严重 令人困惑的错误来源。
我怎样才能在需要时杀死那个 sleep
(或整个子shell)而不造成附带伤害?
如果您有 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
的数组,其中包含进程的两个文件描述符,stdin 和 stdout。
然后您可以随意编写和阅读它们。
如果您的 telnet 在一个命令后没有结束,您可能需要发送第二个命令(例如 echo 'exit' >&${COPROC[1]}
)来断开连接。
您可以尝试在任意文件上用 flock
命令替换睡眠,同时 运行 telnet。代替睡眠的第二个 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
(无space)打开一个新的输出文件描述编号5写入fifo。 echo >&5
写入此 fd。 exec 5>&-
关闭这个 fd。此语法应适用于 ash
.