如何使用SCP或SSH将文件复制到Python中的远程服务器?


Answers:


55

您可以使用以下scp命令调用bash命令(它通过SSH复制文件)subprocess.run

import subprocess
subprocess.run(["scp", FILE, "USER@SERVER:PATH"])
#e.g. subprocess.run(["scp", "foo.bar", "joe@srvr.net:/path/to/foo.bar"])

如果您要创建要在同一Python程序中发送的文件,则需要在用于打开文件subprocess.run的代码with块之外调用命令(.close()如果不使用with块),因此您知道它已从Python刷新到磁盘。

您需要预先生成(在源计算机上)并安装(在目标计算机上)ssh密钥,以便scp自动通过您的公共ssh密钥进行身份验证(换句话说,因此您的脚本不需要输入密码) 。


3
@CharlesDuffy:subprocess.check_call(['scp', srcfile, dest])可以从Python 2.5开始使用,而不是rc = Popen(..).wait(); if rc != 0: raise ..
jfs

1
不需要时,添加ssh键不是一个好的解决方案。例如,如果您需要一次通讯,出于安全原因,您无需设置ssh密钥。
orezvani 2014年

150

要使用Paramiko库在Python中执行此操作(即不通过subprocess.Popen或类似程序包装scp),您将执行以下操作:

import os
import paramiko

ssh = paramiko.SSHClient() 
ssh.load_host_keys(os.path.expanduser(os.path.join("~", ".ssh", "known_hosts")))
ssh.connect(server, username=username, password=password)
sftp = ssh.open_sftp()
sftp.put(localpath, remotepath)
sftp.close()
ssh.close()

(您可能希望处理未知的主机,错误,创建任何必要的目录等)。


14
paramiko也有一个很好的sftp.put(self,localpath,remotepath,callback = None)函数,因此您不必打开write并关闭每个文件。
吉姆·卡罗尔

6
我要指出的是,SFTP与SCP不同。
Drahkar 2012年

4
@Drahkar问题要求通过SSH发送文件。这就是这样做的。
Tony Meyer 2012年

8
注意:要ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())实例化此功能,请在实例化后添加ssh
doda 2012年

1
伙计们,使用服务器密码安全吗?是否可以使用私钥?
Aysennoussi 2014年

32

您可能会使用subprocess模块。像这样:

import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)

destination形式可能在哪里user@remotehost:remotepath。感谢@Charles Duffy指出了我原始答案中的弱点,该答案使用单个字符串参数来指定scp操作shell=True-无法处理路径中的空格。

模块文档中提供了一些错误检查示例,您可能希望与此操作一起执行。

确保设置了正确的凭据,以便可以在计算机之间执行无人值守的无密码scp。已经有一个stackoverflow问题


3
使用subprocess.Popen是正确的事情。向它传递字符串而不是数组(并使用shell = True)是错误的事情,因为这意味着带空格的文件名无法正常工作。
查尔斯·达菲,

2
代替“ sts = os.waitpid(p.pid,0)”,我们可以使用“ sts = p.wait()”。
db42

是否有针对该协议的直接python API的免费执行方式?
lpapp 2013年

1
subprocess.check_call(['scp', myfile, destination])自Python 2.5(2006)
jfs

@JFSebastian:是的,我在12月检查了一下。我的投票至少证明了这一点。:)尽管感谢您的跟进。
lpapp 2014年

12

有两种方法可以解决此问题:

  1. 包装命令行程序
  2. 使用提供SSH功能的Python库(例如-ParamikoTwisted Conch

每种方法都有其自己的怪癖。如果要包装“ ssh”,“ scp”或“ rsync”之类的系统命令,则需要设置SSH密钥以启用无密码登录。您可以使用Paramiko或其他库将密码嵌入脚本中,但是您可能会发现缺少文档令人沮丧,尤其是如果您不熟悉SSH连接的基础知识(例如-密钥交换,代理等)时。不用说,对于这种事情,SSH密钥几乎总是比密码更好的主意。

注意:如果您打算通过SSH传输文件,则很难克服rsync,尤其是如果替代方法是普通的旧式scp。

我使用Paramiko的目的是替换系统调用,但由于其易用性和直接的熟悉性,我发现自己被包裹的命令所吸引。您可能有所不同。一段时间前,我给了海螺一次,但它对我没有吸引力。

如果选择系统调用路径,Python将提供一系列选项,例如os.system或命令/子进程模块。如果使用2.4+版本,我将使用子流程模块。


1
好奇:关于rsync与scp的故事是什么?
Alok

7

达到了相同的问题,但不是“ hacking”或模拟命令行:

在这里找到这个答案。

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')

1
注意-这取决于scp软件包。截至2017年12月,该软件包的最新更新于2015年,版本号为0.1.x. 不知道我是否要添加此依赖项。
查克(Chuck)

2
因此,现在是2018年,他们可能会发布0.11.0版。因此,SCPClient似乎还没有死吗?
Adriano_Pinaffo

5

您可以执行以下操作来处理主机密钥检查

import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")

4

fabric 可用于通过ssh上传文件:

#!/usr/bin/env python
from fabric.api import execute, put
from fabric.network import disconnect_all

if __name__=="__main__":
    import sys
    # specify hostname to connect to and the remote/local paths
    srcdir, remote_dirname, hostname = sys.argv[1:]
    try:
        s = execute(put, srcdir, remote_dirname, host=hostname)
        print(repr(s))
    finally:
        disconnect_all()

2

您可以使用为此目的专门设计的vassal软件包。

您只需要安装vassal并执行

from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()

另外,这将节省您的身份验证凭据,而无需一次又一次地键入它们。


1

我使用sshfs通过ssh挂载远程目录,然后使用shutil复制文件:

$ mkdir ~/sshmount
$ sshfs user@remotehost:/path/to/remote/dst ~/sshmount

然后在python中:

import shutil
shutil.copy('a.txt', '~/sshmount')

此方法的优点是,如果要生成数据而不是本地缓存并发送单个大文件,则可以流式传输数据。


1

如果您不想使用SSL证书,请尝试以下操作:

import subprocess

try:
    # Set scp and ssh data.
    connUser = 'john'
    connHost = 'my.host.com'
    connPath = '/home/john/'
    connPrivateKey = '/home/user/myKey.pem'

    # Use scp to send file from local to host.
    scp = subprocess.Popen(['scp', '-i', connPrivateKey, 'myFile.txt', '{}@{}:{}'.format(connUser, connHost, connPath)])

except CalledProcessError:
    print('ERROR: Connection to host failed!')

1

使用外部资源paramiko;

    from paramiko import SSHClient
    from scp import SCPClient
    import os

    ssh = SSHClient() 
    ssh.load_host_keys(os.path.expanduser(os.path.join("~", ".ssh", "known_hosts")))
    ssh.connect(server, username='username', password='password')
    with SCPClient(ssh.get_transport()) as scp:
            scp.put('test.txt', 'test2.txt')

0

scp通过子进程调用命令不允许在脚本内接收进度报告。pexpect可用于提取该信息:

import pipes
import re
import pexpect # $ pip install pexpect

def progress(locals):
    # extract percents
    print(int(re.search(br'(\d+)%$', locals['child'].after).group(1)))

command = "scp %s %s" % tuple(map(pipes.quote, [srcfile, destination]))
pexpect.run(command, events={r'\d+%': progress})

查看局域网中的python复制文件(linux-> linux)


0

一种非常简单的方法如下:

import os
os.system('sshpass -p "password" scp user@host:/path/to/file ./')

不需要python库(仅适用于os),它可以工作,但是使用此方法依赖于要安装的另一个ssh客户端。如果在另一个系统上运行,可能会导致不良行为。


-3

有点骇人听闻,但以下方法可以工作:)

import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)
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.