我试图通过 SSH 备份一些文件,但我没有备份tar
我想要的文件,而是得到了我的主文件夹。我做了一些进一步的测试,归结为:
ssh root@server /bin/sh -c "cd /boot && ls -l"
令我惊讶的是,其中列出了文件/root
不是/boot
。但是,如果我/bin/sh
从终端运行整个命令,它cd
会正确并打印/boot
文件。
这里发生了什么事?
答案1
ssh 不允许您像您所做的那样精确指定命令作为要传递给远程主机上的 execvp 的一系列参数。相反,它将所有参数连接成一个字符串,并通过远程 shell 运行它们。在我看来,这是 ssh 的一个主要设计缺陷......它在大多数方面都是一个表现良好的 unix 工具,但是当需要指定命令时,它选择使用单个整体字符串而不是 argv,例如它是为 MSDOS 或其他东西设计的!
由于 ssh 会将您的命令作为单个字符串传递给sh -c
,因此您无需提供自己的sh -c
.当你这样做时,结果是
sh -c '/bin/sh -c cd /boot && ls -l'
原始引用丢失。所以用 分隔的命令&&
是:
`/bin/sh -c cd /boot`
`ls -l`
$0
第一个运行带有命令文本“cd”和= 的shell "boot"
。 “cd”命令成功完成,无关紧要$0
,并且/bin/sh -c
表示成功,然后ls -l
发生。
答案2
这是一个引用问题。 ssh 已经运行您在 shell 中传递的命令。当您传递多个参数时,它们之间用空格连接起来以构建一个字符串。因此,您远程运行的远程命令是/bin/sh -c cd /boot && ls -l
(没有引号,因为命令中的引号是由本地 shell 解释的)。
/bin/sh -c cd /boot
运行/bin/sh
并告诉它运行命令cd
并设置$0
为/boot
.一旦完成,父 shell(由 启动的 shell sshd
)就会运行ls -l
。
在您的情况下,只需删除它sh -c
是完全无用的,除非您的远程 shell(如/etc/passwd
或其他密码数据库中所示)不理解此命令。
ssh root@server "cd /boot && ls -l"
如果需要调用不同的 shell,则必须引用远程命令以防止它被 调用的远程 shell 扩展sshd
。例如,如果您的登录 shell 是 dash 并且您想要运行 bash 命令:
ssh root@server 'bash -c "cd ~bob && ls -l"'
答案3
我认为这与 shell 如何解析选项有更多关系。例如,这有效:
$ ssh root@server /bin/sh -c '"cd /boot && ls -l"'
这与您的命令有相同的问题:
$ ssh root@server /bin/sh -c 'cd /boot && ls -l'
如果启用-v
开关,ssh
您可以看到发生了什么:
第一个命令:
debug1:发送命令:/bin/sh -c "cd /boot && ls -l"
第二条命令:
debug1:发送命令:/bin/sh -c cd /boot && ls -l
通常,当通过发送命令时,ssh
您必须特别注意引号以及引号内的引号,因为各个层会将它们剥离。也不必费心发送/bin/sh
。
ssh
一旦您理解了以下内容的引用,您就可以做非常有用的事情。这将在远程服务器上运行该命令,但将结果收集到运行该ssh
命令的系统上的本地文件中:
$ ssh root@server 'free -m' > /tmp/memory.status
或者这样,您在远程服务器上压缩一个目录并在本地系统上创建它:
$ ssh remotehost 'tar zcvf - SOURCEDIR' | cat > DESTFILE.tar.gz
参考
答案4
通常您不必指定要使用的 shell。这有效:
ssh user@host "cd /boot && pwd"
但如果您的默认 shell 有问题,那么只需确保引用全部的命令,包括 shell 调用。以下作品之一
ssh user@host '/bin/sh -c "cd /boot && pwd"'
ssh user@host "/bin/sh -c \"cd /boot && pwd\""