将echo输出重定向至程序输入,程序结束时如何避免管道断裂?
首先得给你理清楚报错的根源:当你的游戏程序./a.out运行结束后,会立刻关闭管道的读端。但你的script.sh还在无限循环执行echo,一个劲往管道的写端塞数据——这时候操作系统会给echo进程发送SIGPIPE(管道断裂)信号,默认行为就是终止进程并吐出echo: write error: Broken pipe的错误提示。
一、避免Broken pipe报错的几种实用方法
方法1:写入失败时自动终止循环
修改脚本逻辑,让echo一旦写入失败(也就是管道被关闭时)就直接跳出循环,从根源上避免重复尝试写入:
#!/bin/bash while true; do # 注意:你原来的%21会生成0-20的数字,游戏需要1-20的话改成下面的写法 num=$((1 + RANDOM % 20)) echo "$num" || break # 只要echo写入失败,立刻终止循环 done
当./a.out退出后,echo第一次尝试写入管道就会失败,返回非零退出码,|| break直接触发循环终止,自然不会再有报错。
方法2:捕获SIGPIPE信号优雅退出
你可以在脚本里自定义SIGPIPE的处理逻辑,比如输出友好提示后正常退出,替代默认的错误输出:
#!/bin/bash # 定义SIGPIPE信号的处理函数 handle_pipe_close() { echo "提示:游戏已结束,停止生成随机数" >&2 exit 0 } # 注册信号捕获规则 trap handle_pipe_close SIGPIPE while true; do num=$((1 + RANDOM % 20)) echo "$num" done
这样当管道关闭时,脚本会自动触发处理函数,输出提示后干净利落地退出,不会出现恼人的Broken pipe报错。
方法3:临时忽略SIGPIPE信号(不推荐)
如果只是单纯想屏蔽报错,也可以在脚本开头忽略SIGPIPE信号,但这种方式下脚本会继续空循环(只是写入失败不会终止进程),有点浪费系统资源,所以仅作为应急方案:
#!/bin/bash trap '' SIGPIPE # 忽略SIGPIPE信号 while true; do num=$((1 + RANDOM % 20)) echo "$num" done
二、如何获取停止输出的信号
触发停止输出的核心信号就是SIGPIPE——当管道的读端被关闭(也就是./a.out退出),操作系统会自动给管道写端的进程(这里是脚本里的echo,或是脚本本身)发送这个信号。
你可以通过trap命令直接捕获它,就像方法2里那样,在信号处理函数里执行清理、输出提示甚至记录日志等操作,然后退出脚本。
另外,也可以通过echo的退出码间接判断:echo收到SIGPIPE时会返回退出码141(128 + 13,SIGPIPE的信号编号是13),你也可以在循环里做针对性判断:
#!/bin/bash while true; do num=$((1 + RANDOM % 20)) echo "$num" exit_code=$? if [ $exit_code -eq 141 ]; then echo "检测到SIGPIPE信号,游戏已结束" >&2 exit 0 fi done
内容的提问来源于stack exchange,提问作者Davos

