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


40

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

ssh root@server /bin/sh -c "cd /boot && ls -l"

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

这里发生了什么事?


1
除非这是一个示例,否则为什么不使用ssh root@server /bin/sh -c "ls -l /boot"
demure

你问好笑。我尝试在评论之前这样做,但仍然没有得到目录清单。
unxnut13年

2
@demure,因为我真的想要tar,并且tar / boot将在结果中包含引导路径,而我不想要
Ambroz Bizjak 2013年

Answers:


34

ssh不允许您像已经做的那样精确地指定命令,因为它是要传递给远程主机上execvp的一系列参数。相反,它将所有参数连接到一个字符串中,并通过远程外壳程序运行它们。在我看来,这是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`

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


2
尽管我同意这样的ssh行为很不幸,但如果不是这样,则必须将其称为sexec,而不是sshssh是的安全版本rsh(在这方面的行为相同)和rlogin(未通过命令行时rsh调用rlogin)。不幸的是,它还没有实现rexec
斯特凡Chazelas

@StephaneChazelas是否曾经有过rexec命令?据我所知,这只是一个C库函数。关于rsh的好处是。在ssh的早期,人人都已经使用rsh编写了大量脚本,因此在rsh中迅速替代它是关键。

我过去肯定使用过它。现在看来,它一定是在HPUX上出现的,因为没有其他的Unices似乎有它,我不得不承认,尽管我觉得它的普及程度更高。
斯特凡Chazelas

谢谢。特别是,这确实有所帮助。因为我正在使用ssh进行隧道传输。我必须做ssh -t machine1'ssh -t machine2“ echo \” 1 && 2 \“”“'才能得到'1 && 2'作为输出。那杀死了几个小时。
ghayes

如果确实要精确地传递命令,例如从“ $ @”传递命令,则可以使用此命令:"`printf "%q " "$@"`"用bash printf引用一个或多个带有外壳转义符的字符串。
山姆·沃特金斯

36

这是一个报价问题。Ssh已经运行了您在shell中传递它的命令。当您传递多个参数时,它们之间将以一个空格连接起来以构建一个字符串。因此,您正在远程运行的远程命令是/bin/sh -c cd /boot && ls -l(无引号,因为命令中的引号由本地外壳程序解释)。

/bin/sh -c cd /boot运行/bin/sh并告诉它运行命令,cd并将其设置$0/boot。完成此操作后,父外壳程序(由发起的外壳程序sshd)就会运行ls -l

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

ssh root@server "cd /boot && ls -l"

如果您需要调用其他外壳程序,则必须引用远程命令以防止它被调用的远程外壳程序扩展sshd。例如,如果您的登录shell是破折号,并且您想运行bash命令:

ssh root@server 'bash -c "cd ~bob && ls -l"'

8

我认为这与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

或这样,您在远程服务器上的目录tar上并在本地系统上创建它:

$ ssh remotehost 'tar zcvf - SOURCEDIR' | cat > DESTFILE.tar.gz

参考文献


4

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

ssh user@host "cd /boot && pwd"

但是,如果您的默认外壳程序有问题,则只需确保引用整个命令,包括外壳程序调用即可。以下任何一项

ssh user@host '/bin/sh -c "cd /boot && pwd"'
ssh user@host "/bin/sh -c \"cd /boot && pwd\""

请注意,虽然cd /boot && pwd可以在主要系列的所有shell(Bourne,csh,rc)中使用,/bin/sh -c "cd /boot && pwd"但如果远程shell 属于非特殊rc系列的外壳,"则无法使用。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.