远程SSH命令中变量无法生效?本地执行脚本远程创建目录失败无报错
核心问题根因
你的here-document写法存在展开逻辑错位,最终执行的操作和预期完全脱节:
- 脚本中
ssh ... << 'EOT'的定界符被单引号包裹,会强制本地shell完全不对here-doc块内的内容做任何变量展开、转义解析,所有内容原封不动传到远程服务器 - 你在块内写的
mkdir -p \$dir_to_create,传到远程的原始内容就是带反斜杠的mkdir -p \$dir_to_create,远程shell解析时反斜杠转义了$符号,最终执行的是创建名字为字面字符串$dir_to_create的目录,根本不会识别你本地定义的/home/rocket/DjangoDir路径 - 你之前尝试的
"\$dir_to_create"、\"$dir_to_create"这类写法,因为本地展开被单引号定界符完全禁用,最终传到远程的还是带转义符的原始字符串,远程环境根本不存在dir_to_create这个变量,路径自然不对 - 因为ssh加了
-q静默参数、-tt强制分配伪终端,连实际创建错目录的输出、权限提示都被完全吞掉,所以你看不到任何报错,只会觉得预期路径没生成。
本质上你的脚本不是没创建目录,是在远程登录用户的默认工作目录(一般是用户家目录)下,建了个名字带$符号的无效目录,和你要找的目标路径根本不是同一个。
可行的修正方案
方案1:调整here-doc展开规则,正确传递本地变量
去掉here-doc定界符的单引号,让本地shell能正常替换块内的变量值,这是和你现有代码最接近的改法:
修改后的核心代码:
sshpass -p ${password} ssh -q -tt user@${remote_ip} << EOT mkdir -p "${dir_to_create}" exit EOT
这种写法下,本地shell会先把块内的${dir_to_create}替换成你定义的实际路径/home/rocket/DjangoDir,再把替换后的内容传给远程执行;加双引号包裹路径参数,也能避免路径包含空格时被shell错误拆分。
方案2:跳过here-doc,直接传递执行命令
如果觉得here-doc的转义规则容易踩坑,可以直接把要执行的命令作为ssh的参数传入,逻辑更直观,转义歧义更少:
sshpass -p ${password} ssh -q -tt user@${remote_ip} "mkdir -p ${dir_to_create}; exit"
本地shell会先完成变量替换,再把整条命令传给远程执行,不存在here-doc的展开规则冲突问题。
方案3:保留单引号定界符的纯传递模式,手动注入变量
如果确实需要保留<< 'EOT'的纯传递特性(避免块内其他内容被本地shell误解析),可以先把本地变量值通过ssh命令传入远程环境,再在here-doc块内调用远程的变量:
sshpass -p ${password} ssh -q -tt user@${remote_ip} "export TARGET_DIR='${dir_to_create}'; bash -s" << 'EOT' mkdir -p "${TARGET_DIR}" exit EOT
这种写法下,here-doc块内的内容完全不做本地展开,所有执行逻辑都在远程完成,不会被本地shell的转义规则干扰。
排查小技巧
测试脚本时可以先临时去掉ssh的-q参数,显示完整的ssh输出和远程命令的执行报错,避免因为静默模式吞掉排查线索。
内容的提问来源于stack exchange,提问作者Rookie1999
相关产品推荐
相关产品推荐

