本地经ip-address1跳转SSH至ip-address2,多命令执行异常求助
这问题我太熟了——核心就是shell的引号解析顺序在搞鬼!
你的原始命令里,外层的双引号"pwd; ls;"会被本地shell先处理,当这条命令传递到ip-address1时,实际上变成了:
ssh -tt -i /root/.ssh/vm_private_key user@ip-address2 pwd; ls;
看到没?分号把命令拆成了两部分:ssh ... pwd和ls。所以pwd会在ip-address2上执行,等ssh连接关闭后,ls就跑到ip-address1上执行了,这就是你看到输出顺序混乱的原因。
下面给你三种靠谱的解决办法:
方法1:用单引号包裹内层SSH命令
把ip-address1上要执行的整个ssh命令用单引号包起来,这样本地shell不会解析里面的双引号和分号,完整传递给ip-address1的shell:
sshpass -p mypassword ssh -tt user@ip-address1 'ssh -tt -i /root/.ssh/vm_private_key user@ip-address2 "pwd; ls;"'
单引号会阻止本地shell对内容做任何解析,确保"pwd; ls;"作为一个整体传递给ip-address2的shell,两条命令都会在目标机器上执行。
方法2:用Here Document(适合多命令场景)
如果以后要执行更多命令,用Here Document会更清晰,可读性更强:
sshpass -p mypassword ssh -tt user@ip-address1 << 'EOF' ssh -tt -i /root/.ssh/vm_private_key user@ip-address2 << 'INNER_EOF' pwd ls INNER_EOF EOF
这里的'EOF'和'INNER_EOF'加了单引号,防止本地shell解析里面的变量或特殊字符,确保所有命令都严格在ip-address2上执行。
方法3:用ProxyJump(最简洁,推荐)
如果你的OpenSSH版本≥7.3(大部分现代系统都满足),直接用-J参数做代理跳转,彻底避免嵌套SSH的引号问题:
sshpass -p mypassword ssh -tt -J user@ip-address1 -i /root/.ssh/vm_private_key user@ip-address2 "pwd; ls;"
-J参数会让本地SSH直接通过ip-address1作为跳板连接到ip-address2,所有命令直接传递给目标机器,逻辑更简单,也不容易出错。
试一下上面的方法,你会看到pwd和ls的输出都在Connection to ip-address2 closed.之前出现,完美解决问题!
内容的提问来源于stack exchange,提问作者Ciaran Whyte

