我试图通过SSH备份一些文件,但没有添加tar
我想要的文件,而是得到了我的主文件夹。我做了一些进一步的测试,归结为:
ssh root@server /bin/sh -c "cd /boot && ls -l"
令我惊讶的是,其中/root
没有列出文件/boot
。但是,如果我/bin/sh
从终端运行整个命令,它将正确地cd
打印/boot
文件。
这里发生了什么事?
我试图通过SSH备份一些文件,但没有添加tar
我想要的文件,而是得到了我的主文件夹。我做了一些进一步的测试,归结为:
ssh root@server /bin/sh -c "cd /boot && ls -l"
令我惊讶的是,其中/root
没有列出文件/boot
。但是,如果我/bin/sh
从终端运行整个命令,它将正确地cd
打印/boot
文件。
这里发生了什么事?
Answers:
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
发生。
ssh
行为很不幸,但如果不是这样,则必须将其称为sexec
,而不是ssh
。ssh
是的安全版本rsh
(在这方面的行为相同)和rlogin
(未通过命令行时rsh
调用rlogin
)。不幸的是,它还没有实现rexec
。
"`printf "%q " "$@"`"
用bash printf
引用一个或多个带有外壳转义符的字符串。
这是一个报价问题。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"'
我认为这与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
通常,您不必指定要使用的外壳。这有效:
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
系列的外壳,"
则无法使用。
ssh root@server /bin/sh -c "ls -l /boot"
?