Bash脚本如何让传入的cat file1命令在脚本内部执行而非调用前求值
现有bash脚本script.sh内容如下:
#!/bin/bash echo "aaa" echo "bbb" ... echo $1 ...
最初使用./script.sh $(cat file1)运行脚本时,cat file1会在脚本启动前就被Bash求值执行,需求是让cat file1延迟到脚本执行到echo $1代码行时才运行,尝试./script.sh $(eval 'cat file1')写法后效果和原写法一致,无法解决问题。
先讲个绕不开的底层逻辑:类Unix系统里任何程序启动时,命令行参数都会在exec调用阶段就完成求值、塞进进程内存空间,脚本一旦启动,$1、$2这些位置参数的值就完全固定了,根本不存在「脚本跑到某一行再动态生成启动参数」的可能。你之前试的./script.sh $(cat file1)、./script.sh $(eval 'cat file1')全都是在父shell里提前做命令替换,cat file1铁定会在脚本启动前跑完,不可能有延迟效果。
要实现跑到echo $1那行才执行cat file1的效果,思路得转个弯:别把cat file1的执行结果当参数传,把cat file1这个命令本身当字符串传进去,在脚本里需要的地方手动触发执行,两步就能搞定:
- 改调用命令,把命令替换的
$()去掉,用单引号把要延迟跑的命令包成普通字符串,防止父shell提前求值:
./script.sh 'cat file1'
- 改脚本里对应位置的代码,把原来的
echo $1换成“先执行传入的命令,再输出结果”的逻辑:- 要是你之后只会传
cat file1这种没特殊语法的简单命令,直接用命令替换就行:
echo "$($1)"- 要是需要支持带管道、重定向、多命令拼起来的复杂语句,用
eval跑兼容性更好:
echo "$(eval "$1")" - 要是你之后只会传
改完再跑脚本,你会看到echo "aaa"、echo "bbb"先输出,跑到改完的这行时才会真正执行cat file1吐文件内容,完全符合你的要求。
注意:
eval会原封不动执行传入的任意shell命令,如果参数来自不可信的外部输入,会有命令注入风险,线上用一定要做好参数校验。
要是你完全改不了script.sh的代码,这个需求从原理上就做不到——毕竟脚本启动的那一刻$1的值就定死了,总不能等跑到echo那行再穿越回启动阶段改参数吧。
内容的提问来源于stack exchange,提问作者Bob5421

