ssh user@remote_host 'bash -s' << 'EOF'
$(declare -p MY_VAR1 MY_VAR2)
echo "Remote received: $MY_VAR1"
EOF
本地 Shell 的工作流程如下:
1. 读取输入:Shell 看到了 << 'EOF' ,知道这是一个 Here Document。
2. 扫描内容:Shell 开始读取 EOF 之间的每一行。
3. 处理特殊语法:
● 遇到 (declare -p ...) :Shell 识别出这是命令替换。为了知道要往数据流里填什么,它必须立即在本地执行 declare -p 命令。
● 遇到 MY_VAR1 :因为被 'EOF' 包裹,Shell 知道这是纯文本,不需要处理,直接原样保留。
4. 组装完成:此时,Shell 已经把 $(...) 替换成了具体的输出(例如 declare -- MY_VAR1="value" )。
5. 启动 SSH:现在,Shell 才会调用 ssh 程序,并把组装好的数据通过标准输入(stdin)传给它。
Bash 的官方文档(Man Page)里明确写着:命令替换(Command Substitution)允许以命令的输出替换(Substitute)命令名。
# 本地 Shell 执行阶段(此时还没有任何网络动作)
1. 本地 Shell 看到 `$(declare -p ...)`。
2. 本地 Shell 立即执行 `declare -p` 命令,获取输出。
3. 本地 Shell 把 `$(...)` 这部分代码,**原地替换**成了变量的定义语句(例如 `declare -- TARGET="/rg"`)。
4. 此时,内存中组装好了要发送的完整脚本。
# SSH 程序执行阶段(网络阶段)
5. SSH 程序才拿到组装好的数据。
6. SSH 开始建立 TCP 连接。
7. SSH 把数据发给远程。
🧪 3. 一个铁证:断网测试
你可以做一个实验来验证这一点:
1. 断开你的网络(或者拔掉网线,断开 Wi-Fi)。
2. 运行你的脚本(包含 (declare -p ...) )。
结果:
● 脚本会先报错: ssh: connect to host ... Connection refused (这是 TCP 连接失败)。
● 但是,如果你在 ssh 命令前面加上 echo 来调试,或者在脚本中打印 remote_script 变量,你会发现 (declare -p ...) 的内容依然被正确执行并展开了。
这证明了: $(...) 的执行不依赖网络,它在本地 Shell 解析脚本时就已经完成了,远早于 SSH 尝试建立 TCP 连接的时刻。
虽然从代码书写上看, (...) 是写在 ssh 命令的参数里的,但从执行流程上看:
(declare -p ...) 是在“本地 Shell 构建命令行参数”的阶段执行的,这个阶段发生在“SSH 程序启动并建立 TCP 连接”的阶段之前。
所以,说它“在 TCP 连接建立之前执行”是完全准确的。