Bash中su命令导致SCP命令变量丢失问题排查
问题
在Ubuntu 18.04.6 LTS系统(Bash 4.4.20(1))中,运行脚本通过su切换到用户me执行SCP时出现变量未解析的错误,但直接在命令行执行相同逻辑的命令却能正常工作。
脚本用于从远程主机拉取文件到本地/tmp,由于root无法无密码SCP,只能切换到有权限的me用户执行。脚本内容如下:
#!/bin/bash writeCmd() { printf '%q ' "$@"; printf '\n'; } printf -v date '%(%Y%m%d)T' -1 user=me host=10.10.10.100 file=myfile target_dir=/path/to/dir/$date # 打印要执行的命令 writeCmd su - me -c 'scp -C me@$host:/$target_dir/$file.txt /tmp/.' su - me -c 'scp -C me@$host:/$target_dir/$file.txt /tmp/.'
执行后输出的命令完全乱掉,变量未被解析:
su - me -c scp-Cme@10.10.10.100://.txt/tmp/.
原因
问题出在单引号的使用:
- Bash中,单引号包裹的内容会被当作纯字符串,当前shell不会解析其中的变量(
$host、$target_dir、$file)。 - 当
su - me -c '...'执行时,切换到me用户的shell后,这个shell里并没有定义host、target_dir这些变量,所以变量展开后变成空值,导致SCP命令的路径拼接混乱,出现//.txt这种错误格式。
而直接在命令行执行时,你大概率是用双引号或者未加引号让变量被当前shell解析,所以能正常工作。
解决方法
把su -c后的单引号换成双引号,让当前shell先解析变量,再把完整的SCP命令传给me用户执行:
修改后的脚本关键部分:
writeCmd su - me -c "scp -C me@$host:/$target_dir/$file.txt /tmp/." su - me -c "scp -C me@$host:/$target_dir/$file.txt /tmp/."
这样当前shell会先将$host、$target_dir、$file替换为实际定义的值,生成完整的SCP命令后,再交给me用户的shell执行,就能正常拉取文件了。
如果你的变量中包含空格、特殊字符等需要转义的内容,可以考虑用printf %q来安全转义,或者将变量通过环境变量传递给su后的shell,但上述双引号的方式在大多数场景下已经足够解决问题。
内容的提问来源于stack exchange,提问作者Pete
相关产品推荐
相关产品推荐

