如何捕获Shell脚本执行的变量替换后原始命令
我之前也碰到过一模一样的问题——用BASH_COMMAND或者历史记录拿命令,结果拿到的全是带变量的模板,完全不是实际跑的命令。下面给你几个实用的方案,刚好适配你提到的脚本场景:
方案1:自定义执行包装函数(最灵活可控)
你可以写一个轻量的包装函数,把要执行的命令作为参数传进去,函数会先打印变量完全展开后的完整命令,再执行原逻辑。这样既能看到实际命令,又不会打乱脚本原有流程。
比如把你脚本里的目标命令改成这样:
# 先定义包装函数,处理特殊字符也更安全 execute_and_log() { printf "实际执行命令: " # 用%q转义空格、引号这类特殊字符,输出的命令可以直接复制执行 printf '%q ' "$@" echo # 执行原命令 "$@" } # 替换原来的命令行 execute_and_log gojson -forcefloats -name="${struct}" -tags=json,mapstructure -pkg=${p} >> "models/${p}/${name%.*}_request.go"
这样每次执行时,就会输出变量替换后的真实命令,比如类似:实际执行命令: gojson -forcefloats -name=GetOrderBook -tags=json,mapstructure -pkg=public >> models/public/get_order_book_request.go
方案2:用Shell的xtrace功能(全局批量记录)
如果想记录脚本里所有执行的命令,不用逐个修改代码行,可以开启Shell的xtrace模式,它会自动在执行每条命令前打印展开后的内容。
在脚本开头加上这两行:
# 把xtrace的输出重定向到日志文件,避免干扰脚本正常输出 exec 2> script_exec_logs.log # 开启xtrace追踪模式 set -x
之后脚本里所有执行的命令,都会以+开头的形式写到script_exec_logs.log里,比如:+ gojson -forcefloats -name=GetOrderBook -tags=json,mapstructure -pkg=public >> models/public/get_order_book_request.go
你直接提取这些行,去掉开头的+ 就是实际执行的命令。
为什么之前的方法不行?
你之前用的「echo last command」方案,依赖的是BASH_COMMAND或者历史记录,但Shell只会保存未展开变量的原始命令字符串,不会自动把变量替换成实际值后再存储。所以拿到的自然是带${struct}这类占位符的模板,而不是真正执行的命令。
内容的提问来源于stack exchange,提问作者Mary

