嵌套SSH执行远程命令的异常行为及执行逻辑解析请求
一、简单案例的执行流程分析
原始命令
ssh -A -t userA@hostA ssh -A -tt userB@hostB "hostname;hostname;hostname"
执行步骤拆解
本地Shell参数解析
本地Shell会先处理命令中的双引号,直接剥除外层双引号,将hostname;hostname;hostname作为普通字符串参数传递给hostA。因此,hostA上实际执行的命令是:ssh -A -tt userB@hostB hostname;hostname;hostnamehostA上的Shell执行
hostA的Shell识别到分号(;)是命令分隔符,会将整个命令拆分为3个独立命令依次执行:- 第一个命令:
ssh -A -tt userB@hostB hostname
建立到hostB的SSH连接,强制分配伪终端(-tt),在hostB上执行hostname,输出hostB;命令执行完成后连接关闭,输出Connection to hostB closed. - 第二个命令:
hostname→ 在hostA本地执行,输出hostA - 第三个命令:
hostname→ 同样在hostA本地执行,输出hostA
- 第一个命令:
这完全匹配你看到的实际结果:
hostB Connection to hostB closed. hostA hostA
二、复杂案例的执行流程分析
原始命令
ssh -A -t userA@hostA ssh -A -t userB@hostB ssh -A -tt userC@hostC \ "hostname \''&&'\' hostname '&&' hostname '&&' hostname && hostname && hostname"
核心逻辑与执行步骤
这个案例的本质是多层Shell对引号和命令分隔符(&&)的逐层解析剥除,最终导致命令被拆分到三层主机上执行:
本地Shell解析
本地Shell处理转义符与引号后,剥除最外层双引号,将命令传递给hostA。此时hostA收到的命令为:ssh -A -t userB@hostB ssh -A -tt userC@hostC hostname '&&' hostname '&&' hostname '&&' hostname && hostname && hostnamehostA上的Shell执行
hostA的Shell识别到未被引号包裹的&&,将命令拆分为3部分:- 第一部分:
ssh -A -t userB@hostB ssh -A -tt userC@hostC hostname '&&' hostname '&&' hostname '&&' hostname - 第二、三部分:
hostname、hostname(后续在hostA本地执行)
- 第一部分:
hostB上的Shell执行
hostB收到第一部分命令后,同样识别到未被正确包裹的命令逻辑,将其拆分为:- 第一部分:
ssh -A -tt userC@hostC hostname '&&' hostname - 第二、三部分:
hostname、hostname(后续在hostB本地执行)
- 第一部分:
hostC上的Shell执行
hostC收到的命令是hostname '&&' hostname,由于'&&'被单引号包裹,不会被当作命令分隔符,hostname忽略无效参数,连续执行两次,输出hostC、hostC;执行完成后连接关闭。逐层返回执行剩余命令
hostC连接关闭后,hostB执行剩余的两个hostname,输出hostB、hostB,随后连接关闭;最后hostA执行剩余的两个hostname,输出hostA、hostA,连接关闭。
这就对应了你看到的实际结果:
hostC hostC Connection to hostC closed. hostB hostB Connection to hostB closed. hostA hostA Connection to hostA closed.
核心问题与解决方案
核心原因
嵌套SSH命令时,若未用足够的引号保护完整的远程命令串,每一层的Shell都会解析命令中的分隔符(;、&&),将原本要传递给内层的命令拆分到当前层执行。
正确写法
要让所有命令在最内层主机执行,需用单引号包裹每一层的远程命令,避免被上层Shell解析分隔符。比如第一个案例的修正写法:
ssh -A -t userA@hostA 'ssh -A -tt userB@hostB "hostname;hostname;hostname"'
外层单引号保护hostA上的完整命令,hostA的Shell再通过内层双引号保护传递给hostB的命令串,确保所有hostname都在hostB执行。
内容的提问来源于stack exchange,提问作者gleport

