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

Amb*_*jak 42 shell ssh cd-command

我试图通过 SSH 备份一些文件,但tar我得到了我的主文件夹,而不是我想要的那些。我做了一些进一步的测试,归结为:

ssh root@server /bin/sh -c "cd /boot && ls -l"
Run Code Online (Sandbox Code Playgroud)

令我惊讶的是,其中列出了/rootnot中的文件/boot。但是,如果我/bin/sh从终端运行整个命令,它就会正确地cd打印/boot文件。

这里发生了什么事?

小智 39

ssh 不允许您像您所做的那样将命令精确指定为要传递给远程主机上的 execvp 的一系列参数。相反,它将所有参数连接成一个字符串,并通过远程 shell 运行它们。在我看来,这是 ssh 中的一个主要设计缺陷......它在大多数方面都是一个表现良好的 unix 工具,但是当需要指定一个命令时,它选择使用单个整体字符串而不是 argv,例如它是为 MSDOS 或其他东西设计的!

由于 ssh 会将您的命令作为单个字符串传递给sh -c,因此您无需提供自己的sh -c. 当你这样做时,结果是

sh -c '/bin/sh -c cd /boot && ls -l'
Run Code Online (Sandbox Code Playgroud)

原始引用丢失。所以由 分隔的命令&&是:

`/bin/sh -c cd /boot`
`ls -l`
Run Code Online (Sandbox Code Playgroud)

第一个运行带有命令文本“cd”和$0=的 shell "boot"。“cd”命令成功完成,$0无关紧要,/bin/sh -c表示成功,然后ls -l发生。

  • 虽然我同意 `ssh` 的行为很不幸,如果不是这样,它就必须被称为 `sexec`,而不是 `ssh`。`ssh` 是 `rsh`(在这方面的行为相同)和 `rlogin`(`rsh` 在未通过命令行时称为 `rlogin`)的安全版本。不幸的是,它也没有实现`rexec`。 (2认同)

Gil*_*il' 36

?这是一个引用问题。Ssh 已经运行了您在 shell 中传递给它的命令。当您传递多个参数时,它们之间用空格连接以构建一个字符串。所以你远程运行的远程命令是/bin/sh -c cd /boot && ls -l(没有引号,因为你的命令中的引号是由本地shell解释的)。

/bin/sh -c cd /boot运行/bin/sh并告诉它运行命令cd并设置$0/boot. 完成此操作后,父 shell(由 启动的sshd)运行ls -l

在您的情况下,只需删除sh -c完全无用的 除非您的远程外壳程序(如/etc/passwd或其他密码数据库所示)不理解此命令。

ssh root@server "cd /boot && ls -l"
Run Code Online (Sandbox Code Playgroud)

如果您需要调用不同的 shell,则必须引用远程命令以防止它被sshd. 例如,如果您的登录 shell 是 dash 并且您想运行 bash 命令:

ssh root@server 'bash -c "cd ~bob && ls -l"'
Run Code Online (Sandbox Code Playgroud)


slm*_*slm 10

我认为这更多地与 shell 如何解析选项有关。例如,这有效:

$ ssh root@server /bin/sh -c '"cd /boot && ls -l"'
Run Code Online (Sandbox Code Playgroud)

这与您的命令有相同的问题:

$ ssh root@server /bin/sh -c 'cd /boot && ls -l'
Run Code Online (Sandbox Code Playgroud)

如果您启用-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
Run Code Online (Sandbox Code Playgroud)

或者这个,你在远程服务器上tar一个目录并在本地系统上创建它:

$ ssh remotehost 'tar zcvf - SOURCEDIR' | cat > DESTFILE.tar.gz
Run Code Online (Sandbox Code Playgroud)

参考


tyl*_*erl 5

通常,您不必指定要使用的 shell。这有效:

ssh user@host "cd /boot && pwd"
Run Code Online (Sandbox Code Playgroud)

但是,如果您的默认 shell 有问题,那么只需确保引用整个命令,包括 shell 调用。以下任一作品

ssh user@host '/bin/sh -c "cd /boot && pwd"'
ssh user@host "/bin/sh -c \"cd /boot && pwd\""
Run Code Online (Sandbox Code Playgroud)