如何使用scp命令与第二个远程主机进行文件传输

97
我想知道是否有办法让我从本地机器通过远程1主机直接从远程2主机传输文件到本地机器。网络只允许从远程1主机连接到远程2主机。此外,远程1主机和远程2主机都无法将文件传输到我的本地机器。像这样的东西是否存在:
scp user1@remote1:user2@remote2:file .
在第一个窗口中:ssh remote1,然后scp remot2:file。
在第二个shell中:scp remote1:file .
在第一个窗口中:rm file; logout
我可以编写脚本执行所有这些步骤,但如果有直接的方法,我宁愿使用它。谢谢。
编辑:我想到了一些方式,例如打开SSH隧道,但不确定要在哪里使用哪些值。
目前,在我的本地机器上的$HOME/.ssh/config中,为了访问远程1主机,我有以下设置。
Host remote1
   User     user1
   Hostname localhost
   Port     45678

在连接到remote1后,要访问remote2,则需要使用标准的本地DNS和22端口。我应该在remote1上输入什么,或者在localhost上做出哪些更改?

8个回答

101

我不知道有没有一种直接复制文件的方法,但是如果您可以在后台运行一个SSH实例,只需保持一个端口转发隧道打开,那么您可以使用一个命令来复制文件。

像这样:

# First, open the tunnel
ssh -L 1234:remote2:22 -p 45678 user1@remote1
# Then, use the tunnel to copy the file directly from remote2
scp -P 1234 user2@localhost:file .

注意,在实际的scp命令中,您连接的是user2@localhost,因为在本地主机上端口1234上第一个ssh实例正在侦听来自remote2的转发连接。还要注意,您不需要针对每个后续文件复制运行第一个命令; 您可以让它保持运行状态。


1
谢谢,这似乎接近我所需要的。所以我创建了隧道,指纹与服务器的匹配,但是我遇到了“权限被拒绝(公钥)”的错误。我认为我需要询问我的网络/系统管理员为什么它不起作用。 - Danosaure
3
我不得不将-p 45678改成-p 22,因为我的remote1 SSH正在监听22端口。谢谢! - Montaro
我还必须使用-p 22而不是-p 45678。此外,scp -P 1234 ...对我无效。我得到了ssh:connect to host localhost port 1234: Connection refused的错误。当我尝试使用scp -P 22 ...时,它可以工作,但它会将文件复制到“remote1”而不是我的本地机器(“remote2”)。 - sinner
有没有相应的 UI 工具? - ExploringApple

77

使用双重 ssh

即使在你的复杂情况下,你也可以使用单个命令行处理文件传输,只需使用 ssh 即可;-)
如果 remote1 无法连接到 localhost,这将非常有用:

ssh user1@remote1 'ssh user2@remote2 "cat file"' > file

tar

但是你会失去文件属性(所有权、权限等)。

然而,tar 是保持这些文件属性的好帮手:

ssh user1@remote1 'ssh user2@remote2 "cd path2; tar c file"' | tar x

您还可以进行压缩以减少网络带宽:

ssh user1@remote1 'ssh user2@remote2 "cd path2; tar cj file"' | tar xj

使用 tar 命令还可以通过基本的 ssh 传输一个递归的目录:

ssh user1@remote1 'ssh user2@remote2 "cd path2; tar cj ."' | tar xj

ionice

如果文件很大,你不想影响其他重要的网络应用程序,又想利用scprsync工具提供的网络吞吐限制(例如:scp -l 1024 user@remote:file 不会使用超过1兆位/秒),那么一个解决方法是使用ionice来执行单个命令行:

ionice -c2 -n7 ssh u1@remote1 'ionice -c2 -n7 ssh u2@remote2 "cat file"' > file

注意:在旧的发行版上可能没有ionice命令。


谢谢您提供的所有描述,但我认为Dolda2000的解决方案更简单。这正是我一直在尝试但无法弄清楚的事情。 - Danosaure
6
这个回答非常出色,值得得到更多的投票。在我看来,这比被接受的答案要容易得多。 - Rik Smith-Unna
2
我同意这是比被接受的答案更好的解决方案。这样,连接会自动清理。 - Doug
谢谢,非常好的答案!那么另一种方式呢,从本地复制到远程? - DomTomCat

33

这个就可以解决问题:

scp -o 'Host remote2' -o 'ProxyCommand ssh user@remote1 nc %h %p' \ 
    user@remote2:path/to/file .

要直接从主机remote2 SCP文件,请将两个选项(HostProxyCommand)添加到您的~/.ssh/config文件中(请参见 superuser 上的答案)。然后您可以运行:

scp user@remote2:path/to/file .

您可以从本地计算机访问而无需考虑remote1


不错的方法!虽然在命令行中启动时似乎不真正需要-o 'Host remote2'(即只复制一次而不触及~/.ssh/config) - Mike
一样的情况。这对我来说没有 -o 'Host remote2' 也可以工作。谢谢。 - sinner
在将这些行添加到我的.ssh/config文件后,我能够运行ssh remote2。但是因为我的兴趣是将一个目录推送到remote2,所以我选择了:scp -r SourceDirectory remote2:DestinationDirectory - analyst_47

8
使用 openssh 版本 7.3 及以上版本很容易实现。在配置文件中使用 ProxyJump 选项即可。
# Add to ~/.ssh/config 
Host bastion
    Hostname bastion.client.com
    User userForBastion
    IdentityFile ~/.ssh/bastion.pem

Host appMachine
    Hostname appMachine.internal.com
    User bastion
    ProxyJump bastion                   # openssh 7.3 version new feature ProxyJump
    IdentityFile ~/.ssh/appMachine.pem. #no need to copy pem file to bastion host  

运行登录或复制的命令

ssh appMachine   # no need to specify any tunnel. 
scp helloWorld.txt appMachine:.   # copy without intermediate jumphost/bastion host copy.** 

当然,你可以使用选项“-J”来指定跳板机作为ssh命令的参数,如果没有在配置文件中配置的话。

注意:截至目前,scp似乎不支持“-J”标志(我在man手册中找不到),但是在配置文件设置的情况下上述scp命令可用。


1
如果跳板服务器仅用于代理(即没有不同的IdentityFile等),则无需将其添加到配置文件中,只需在appMachine部分中添加ProxyJump bastion.client.com即可。 - Tomer Cohen

3

scp 中有一个新选项,专门为此工作添加了一个非常方便的功能:-3

TL;DR 对于已在 SSH 配置文件中设置身份验证的当前主机,只需执行以下操作:

scp -3 remote1:file remote2:file

你的scp必须是最新版本。

所有其他提到的技术都需要你在remote1和remote2之间设置身份验证,这并不总是一个好主意。
参数-3表示你想通过使用当前主机作为中介来移动两个远程主机之间的文件,并且该主机实际上对两个远程主机进行身份验证,所以它们不必互相访问。
您只需要在ssh配置文件中设置身份验证,这非常容易并且有很好的文档记录,然后只需运行TL;DR中的命令即可。

此答案的来源为https://superuser.com/a/686527/713762


1
这个配置对我来说很好:

Host jump
   User username
   Hostname jumphost.yourorg.intranet
Host production
   User username
   Hostname production.yourorg.intranet
   ProxyCommand ssh -q -W %h:%p jump

然后是命令

scp myfile production:~

myfile复制到production机器。

0

对于Olibre在这里提供的解决方案的小补充,我使用了this的资源。

就像你有三种使用tar从远程主机复制到本地的方式一样,在双重ssh情况下,以下方法适用于本地主机到远程主机的复制:(在文件需要复制的目录中运行它们,否则使用完整路径/文件名)

无压缩传输单个文件:

tar c filename |  ssh user1@remote1 'ssh -Y user2@remote2 "path2 && tar x"'

使用压缩功能传输单个文件:

tar cj filename |  ssh user1@remote1 'ssh -Y user2@remote2 "path2 && tar xj"'

递归目录传输:
tar cj . |  ssh user1@remote1 'ssh -Y user2@remote2 "path2 && tar xj"'

这里的 && 符号可以防止命令在第一部分命令不成功的情况下继续执行 - 例如,如果目录不存在或者源/目标路径名有误。

0
一个更简单的方法:
scp -o 'ProxyJump your.jump.host' /local/dir/myfile.txt remote.internal.host:/remote/dir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接