Bash脚本多次读取OpenSSL输出管道时冻结,如何解决?
解决OpenSSL管道二次读取冻结问题
问题场景
使用OpenSSL的s_client命令连接XMPP服务器,通过两个命名管道$topipe和$frompipe分别传递输入和输出。首次从$frompipe读取服务器响应正常,但二次读取时脚本冻结,相关代码如下:
openssl s_client \ -starttls xmpp \ -xmpphost "$domain" \ -connect "$host:$port" \ -quiet \ < $topipe \ > $frompipe & echo "somedata" > $topipe # 第一次读取正常 read -t 1 serverresponse < "$frompipe" # 中间处理逻辑... # 此处冻结 read -t 1 serverresponse < "$frompipe"
问题原因
每次执行read < "$frompipe"时,Bash会临时打开管道的读端,读取完成后立即关闭该读端。当第一次读取完成并关闭读端后,管道$frompipe的所有读端都已关闭,此时OpenSSL进程向管道写入数据时会触发SIGPIPE信号,导致OpenSSL进程直接终止。第二次读取时,管道已没有活跃的写进程,脚本会陷入等待状态(即便设置了超时也可能出现异常阻塞)。
另外,echo "somedata" > $topipe写完后会立即关闭$topipe的写端,可能导致OpenSSL的stdin收到EOF,提前终止进程,也会引发后续读取问题。
解决方案
1. 持久化管道文件描述符
通过exec命令提前打开管道的读/写文件描述符,保持管道两端持续打开,避免多次打开关闭导致的进程终止:
# 先创建命名管道(如果还没创建) mkfifo topipe frompipe # 打开frompipe的读端到文件描述符3 exec 3<"$frompipe" # 打开topipe的写端到文件描述符4 exec 4>"$topipe" # 启动OpenSSL进程 openssl s_client \ -starttls xmpp \ -xmpphost "$domain" \ -connect "$host:$port" \ -quiet \ <&4 \ >&3 & # 通过持久化描述符写入数据 echo "somedata" >&4 # 第一次读取 read -t 1 serverresponse <&3 # 中间处理逻辑... # 第二次读取(正常工作) read -t 1 serverresponse <&3 # 最后关闭文件描述符 exec 3<&- exec 4>&-
2. 在单个子shell中完成所有读取操作
将所有读取逻辑放入同一个子shell,保持管道读端持续打开,避免多次关闭读端:
mkfifo topipe frompipe # 启动OpenSSL进程 openssl s_client \ -starttls xmpp \ -xmpphost "$domain" \ -connect "$host:$port" \ -quiet \ < $topipe \ > $frompipe & # 保持topipe写端打开(避免OpenSSL提前收到EOF) exec 4>"$topipe" echo "somedata" >&4 # 在子shell中完成所有读取,保持frompipe读端打开 ( read -t 1 serverresponse # 中间处理逻辑... read -t 1 serverresponse ) < "$frompipe" exec 4>&-
内容的提问来源于stack exchange,提问作者Hid-K
相关产品推荐
相关产品推荐

