为什么 `cd` 命令不能通过 SSH 工作?

为什么 `cd` 命令不能通过 SSH 工作?

我试图通过 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\""

相关内容