在Bash中捕获命令的标准输出与错误输出,如何避免使用eval?
问题:规避eval实现命令stdout/stderr捕获的最优方案
关于eval的使用存在诸多警告,多数开发者认为应尽量规避该命令。但尝试多种方法后,发现不使用eval就会出现大量错误。需求是捕获命令的标准输出(stdout)与错误输出(stderr),当前实现的run函数如下:
run() { ... result=$(eval "$*" 2> "tee" -a "$_logfile" >&2) ... }
另外,使用"$*"而非"$@"是为了避免shellcheck的SC2294警告(eval会抵消数组的优势),给tee加引号是为了避免SC2238警告(重定向到/从命令名而非文件)。测试的命令示例为:
run "yq --input-format xml --output-format props <\"$xmlfile\""
想知道是否有更优的实现方式?
更优实现方案
方案1:数组传递命令(推荐,无eval)
直接将命令作为数组参数传递给run函数,既保留参数边界,又彻底规避eval,同时解决shellcheck相关警告:
run() { local _logfile="your_log_path.log" # 捕获stdout到result,stderr同时写入日志和原stderr通道 result="$("$@" 2> >(tee -a "$_logfile" >&2))" }
调用时无需手动转义引号,写法更直观:
run yq --input-format xml --output-format props < "$xmlfile"
优势:
- 完全消除
eval带来的安全风险和解析歧义 $@会保留每个参数的原始边界,不会因空格、特殊字符导致参数拆分错误- 进程替换
>(tee ...)实现stderr分流,逻辑清晰,不会触发SC2238警告
方案2:支持复杂shell语法的替代方案
如果需要处理包含重定向、管道等shell内置语法的复杂命令,可使用bash -c替代eval,安全性更高:
run() { local _logfile="your_log_path.log" # 用子shell执行命令字符串,保留shell语法支持 result="$("$SHELL" -c "$1" 2> >(tee -a "$_logfile" >&2))" }
调用时用单引号包裹命令字符串,避免变量提前展开:
run 'yq --input-format xml --output-format props < "$xmlfile"'
内容的提问来源于stack exchange,提问作者duise
相关产品推荐
相关产品推荐

