Bash中关闭stdout后,函数如何返回值?
关闭stdout后如何让Bash函数正常返回值?
背景
为了避免脚本被CRON调用时写入/var/log/cron.log,我在多个Bash脚本开头加入了以下代码:
# Not an interactive use. if [[ ! -t 0 && ! -t 1 ]]; then #echo "The script is not called interactively. Close stdout and stderr." # Close standard output file descriptor exec 1<&- # Close standard error file descriptor exec 2<&- fi
但这段代码存在副作用:脚本内函数通过echo返回的值无法被$(...)调用行读取。比如下面的示例脚本,运行后不会输出Test is true:
#! /bin/bash # Not an interactive use. if [[ ! -t 0 && ! -t 1 ]]; then #echo "The script is not called interactively. Close stdout and stderr." # Close standard output file descriptor exec 1<&- # Close standard error file descriptor exec 2<&- fi function myTest() { echo true } if [[ $(myTest) == true ]]; then echo Test is true; fi
我找到一种用全局变量的临时解决方法,但不够优雅:
declare gResult; function myTest() { gResult=true; } myTest if [[ "$gResult" == true ]]; then echo Test is true; fi
可行解决方案
1. 重定向到/dev/null而非关闭文件描述符
核心问题是exec 1<&-直接关闭了stdout文件描述符,导致$(...)无法捕获子shell的输出。只需将关闭操作改为重定向到/dev/null,既能避免输出写入cron日志,又能保留stdout的存在性,让函数的echo输出可以被正常捕获:
# 非交互式调用处理 if [[ ! -t 0 && ! -t 1 ]]; then # 将stdout和stderr重定向到/dev/null,而非关闭 exec 1>/dev/null exec 2>/dev/null fi
修改后,原示例脚本中$(myTest)能正常拿到true,执行echo Test is true,但该输出会被导向/dev/null,不会出现在/var/log/cron.log中,完全满足需求。
2. 利用函数的退出状态返回简单值
如果函数只需返回布尔状态或小整数结果,直接使用Bash函数的退出状态(return语句)即可,完全不需要依赖stdout:
function myTest() { # 返回0表示成功/true,非0表示失败/false return 0 } # 直接通过函数的退出状态判断 if myTest; then echo Test is true; fi
这种方式完全避开了stdout的问题,代码更简洁,适合状态类返回场景。
3. 备份文件描述符(适合必须关闭stdout的场景)
如果业务场景要求必须关闭stdout,可以先将stdout备份到一个备用文件描述符,让函数输出到这个备份描述符,捕获时从该描述符读取:
# 非交互式调用处理 if [[ ! -t 0 && ! -t 1 ]]; then # 备份stdout到文件描述符3,再关闭原stdout和stderr exec 3>&1 exec 1<&- exec 2<&- fi function myTest() { # 输出到备份的文件描述符3 echo true >&3 } # 从文件描述符3捕获函数输出 if [[ $(myTest 2>&1) == true ]]; then # 若需输出内容,同样指向备份的描述符 echo Test is true >&3 fi
内容的提问来源于stack exchange,提问作者ballatom
相关产品推荐
相关产品推荐

