如何在python中进行scp?


158

在Python中scp文件的最pythonic方式是什么?我知道的唯一路线是

os.system('scp "%s" "%s:%s"' % (localfile, remotehost, remotefile) )

这是一种hack,并且无法在类似Linux的系统之外运行,并且需要Pexpect模块的帮助来避免出现密码提示,除非您已经为远程主机设置了无密码的SSH。

我知道Twisted的conch,但我希望避免通过低级ssh模块自己实现scp。

我知道paramiko,支持SSH和SFTP的Python模块;但它不支持SCP。

背景:我正在连接到不支持SFTP但支持SSH / SCP的路由器,因此不能选择SFTP。

编辑:这是如何使用SCP或SSH将文件复制到Python中的远程服务器的重复项但是,该问题并未给出处理来自Python内部键的特定于scp的答案。我希望找到一种运行类似代码的方法

import scp

client = scp.Client(host=host, user=user, keyfile=keyfile)
# or
client = scp.Client(host=host, user=user)
client.use_system_keys()
# or
client = scp.Client(host=host, user=user, password=password)

# and then
client.transfer('/etc/local/filename', '/etc/remote/filename')

Answers:


106

尝试使用ParamikoPython scp模块。它很容易使用。请参见以下示例:

import paramiko
from scp import SCPClient

def createSSHClient(server, port, user, password):
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(server, port, user, password)
    return client

ssh = createSSHClient(server, port, user, password)
scp = SCPClient(ssh.get_transport())

然后致电scp.get()scp.put()进行SCP操作。

SCPClient代码


3
除了该模块实际上不使用paramiko之外,最终很多代码最终只能scp调用scp仅在* nix上有效的命令行。
Nick Bastin 2012年

8
同意,您在源代码中看到的是对scp -t或的远程调用scp -f(“至”和“从”模式,用于此服务器端目的)。本质上,这就是scp的工作方式-它依赖于服务器上现有的scp的另一个副本。本文对此进行了很好的解释:blogs.oracle.com/janp/entry/how_the_scp_protocol_works
13年

8
该解决方案为我提供了两个警告:1.这些是我使用的import语句:import paramiko from scp import SCPClient2.以下是scp.get()命令的示例:scp.get(r'/nfs_home/appers/xxxx/test2.txt', r'C:\Users\xxxx\Desktop\MR_Test')
Chris Nielsen

这不仅效果很好,而且还可以利用SSH密钥(如果存在)。
马赛·威尔逊

请注意pip install scp,在撰写本文时,您还可以从PyPI:0.10.2版本安装此库。
Andrew B.

15

您可能对尝试Pexpect源代码)感兴趣。这将使您能够处理交互式提示输入密码。

这是主要网站上的一些用法示例(用于ftp):

# This connects to the openbsd ftp site and
# downloads the recursive directory listing.
import pexpect
child = pexpect.spawn ('ftp ftp.openbsd.org')
child.expect ('Name .*: ')
child.sendline ('anonymous')
child.expect ('Password:')
child.sendline ('noah@example.com')
child.expect ('ftp> ')
child.sendline ('cd pub')
child.expect('ftp> ')
child.sendline ('get ls-lR.gz')
child.expect('ftp> ')
child.sendline ('bye')

11

您也可以查看paramiko。还没有scp模块,但是它完全支持sftp。

[EDIT]抱歉,错过了提到paramiko的那一行。以下模块只是paramiko的scp协议的实现。如果您不想使用paramiko或conch(我知道用于python的唯一ssh实现),则可以对其进行重做以使用管道在常规ssh会话上运行。

scp.py for paramiko


您是说附加解决方案将安全地将文件传输到任何运行sshd的计算机上,即使它没有运行sftpd吗?这正是我在寻找的内容,但是我无法从您的评论中看出这是否只是将sftp封装在类似scp的外观中。
Michael Gundlach

2
这将使用任何运行sshd的计算机传输文件,该计算机的PATH中包含scp(scp并不是ssh规范的一部分,但是相当普遍)。这将在服务器上调用“ scp -t”,并使用scp协议来传输文件,这与sftp无关。
JimB

1
请注意,我使用openssh的scp实现作为模型,因此不能保证它可以与其他版本一起使用。某些版本的sshd也可能使用scp2协议,该协议与sftp基本上相同。
JimB

您能否看到以下问题:stackoverflow.com/questions/20111242/… 我想知道paramiko是使用子进程还是其他类似套接字的东西。由于克隆/叉问题,我使用subprocess.check_output('ssh blah@blah.com“ cat / data / file *”)遇到内存问题,并且想知道paramiko是否会遇到相同的问题?
保罗

1
@Paul:paramiko将不会遇到相同的问题,因为它不使用子流程。
JimB

9

找不到直接的答案,并且此“ scp.Client”模块不存在。相反,适合我:

from paramiko import SSHClient
from scp import SCPClient

ssh = SSHClient()
ssh.load_system_host_keys()
ssh.connect('example.com')

with SCPClient(ssh.get_transport()) as scp:
   scp.put('test.txt', 'test2.txt')
   scp.get('test2.txt')

6

如果在win32上安装腻子,则会得到一个pscp(putty scp)。

因此您也可以在win32上使用os.system hack。

(并且您可以使用putty-agent进行密钥管理)


抱歉,这只是一个hack(但是您可以将其包装在python类中)


'nix一个人起作用的唯一原因是,您在路径上有SCP。正如Blauohr所指出的那样,修复起来并不难。+1
ojrac

6

您可以使用包子进程和命令调用来从外壳程序使用scp命令。

from subprocess import call

cmd = "scp user1@host1:files user2@host2:files"
call(cmd.split(" "))

2
这与提问者提到的基本案例有何本质区别?是的,子进程比os.system更好,但是在两种情况下,它都无法在类似linux的系统之外运行,存在密码提示问题等
。– Foon

5

到目前为止,最好的解决方案可能是 AsyncSSH

https://asyncssh.readthedocs.io/en/latest/#scp-client

async with asyncssh.connect('host.tld') as conn:
    await asyncssh.scp((conn, 'example.txt'), '.', recurse=True)

1
你好 您声称AsyncSSH是最好的,但您可以发表声明还是2个来支持此声明?也许将其与scp()区别开来,或者将其与您的用例无关。(这就是说,代码要少得多-至少在您的示例中如此)
Scott Prive

2
@Crossfit_and_Beer你好。因此,我说“可能是最好的”,我将判断权交给任何人:)我现在没有多少时间将AsyncSSH与其他库进行比较。但是很快:它简单易用,异步,可以维护,维护者非常好并且乐于助人,它非常完整并且有据可查。
卢瓦克

5

看一下fabric.transfer

from fabric import Connection

with Connection(host="hostname", 
                user="admin", 
                connect_kwargs={"key_filename": "/home/myuser/.ssh/private.key"}
               ) as c:
    c.get('/foo/bar/file.txt', '/tmp/')

2
你可以再详细一点吗?
尼克T

4

自问这个问题以来已经有一段时间了,与此同时,另一个可以处理此问题的库也出现了:您可以使用Plumbum库中包含的复制功能:

import plumbum
r = plumbum.machines.SshMachine("example.net")
   # this will use your ssh config as `ssh` from shell
   # depending on your config, you might also need additional
   # params, eg: `user="username", keyfile=".ssh/some_key"`
fro = plumbum.local.path("some_file")
to = r.path("/path/to/destination/")
plumbum.path.utils.copy(fro, to)

如何使用Plumbum输入私钥的密码?p
ekta 2014年

@ekta:在命令行上将提示您输入。如果您想通过自己的代码提供它,我认为lumbum的API不支持此功能。但是铅SshMachine的确可以访问ssh-agent,因此,如果您只是想避免每次都键入它,那是一种方法。您还可以在plumbum的github页面上提交功能请求。
pmos

2

如果您使用* nix,则可以使用sshpass

sshpass -p password scp -o User=username -o StrictHostKeyChecking=no src dst:/path

2
import paramiko

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

client.connect('<IP Address>', username='<User Name>',password='' ,key_filename='<.PEM File path')

#Setup sftp connection and transmit this script 
print ("copying")

sftp = client.open_sftp() 
sftp.put(<Source>, <Destination>)


sftp.close()

如果您在答案中添加了一些注释,说明为什么您的解决方案是一个好的解决方案,并且可以帮助读者将其与其他可用解决方案进行比较,则将很有帮助。仅仅发布代码并不能总是帮助学习者知道如何识别更好的解决方案。
Brian Tompsett-汤莱恩

这是SFTP,而不是SCP。
马丁·普里克里

1

嗯,也许另一个选择是使用sshfs之类的东西(Mac也有sshfs)。路由器安装后,您可以直接复制文件。我不确定这是否适用于您的特定应用程序,但这是一个方便的好方法。



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.