标签归档:scp

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

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

我在本地计算机上有一个文本文件,该文件由cron中运行的每日Python脚本生成。

我想添加一些代码,以使该文件通过SSH安全地发送到我的服务器。

I have a text file on my local machine that is generated by a daily Python script run in cron.

I would like to add a bit of code to have that file sent securely to my server over SSH.


回答 0

您可以使用以下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密钥进行身份验证(换句话说,因此您的脚本不需要输入密码) 。

You can call the scp bash command (it copies files over SSH) with 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"])

If you’re creating the file that you want to send in the same Python program, you’ll want to call subprocess.run command outside the with block you’re using to open the file (or call .close() on the file first if you’re not using a with block), so you know it’s flushed to disk from Python.

You need to generate (on the source machine) and install (on the destination machine) an ssh key beforehand so that the scp automatically gets authenticated with your public ssh key (in other words, so your script doesn’t ask for a password).


回答 1

要使用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()

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

To do this in Python (i.e. not wrapping scp through subprocess.Popen or similar) with the Paramiko library, you would do something like this:

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

(You would probably want to deal with unknown hosts, errors, creating any directories necessary, and so on).


回答 2

您可能会使用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问题

You’d probably use the subprocess module. Something like this:

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

Where destination is probably of the form user@remotehost:remotepath. Thanks to @Charles Duffy for pointing out the weakness in my original answer, which used a single string argument to specify the scp operation shell=True – that wouldn’t handle whitespace in paths.

The module documentation has examples of error checking that you may want to perform in conjunction with this operation.

Ensure that you’ve set up proper credentials so that you can perform an unattended, passwordless scp between the machines. There is a stackoverflow question for this already.


回答 3

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

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

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

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

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

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

There are a couple of different ways to approach the problem:

  1. Wrap command-line programs
  2. use a Python library that provides SSH capabilities (eg – Paramiko or Twisted Conch)

Each approach has its own quirks. You will need to setup SSH keys to enable password-less logins if you are wrapping system commands like “ssh”, “scp” or “rsync.” You can embed a password in a script using Paramiko or some other library, but you might find the lack of documentation frustrating, especially if you are not familiar with the basics of the SSH connection (eg – key exchanges, agents, etc). It probably goes without saying that SSH keys are almost always a better idea than passwords for this sort of stuff.

NOTE: its hard to beat rsync if you plan on transferring files via SSH, especially if the alternative is plain old scp.

I’ve used Paramiko with an eye towards replacing system calls but found myself drawn back to the wrapped commands due to their ease of use and immediate familiarity. You might be different. I gave Conch the once-over some time ago but it didn’t appeal to me.

If opting for the system-call path, Python offers an array of options such as os.system or the commands/subprocess modules. I’d go with the subprocess module if using version 2.4+.


回答 4

达到了相同的问题,但不是“ 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')

Reached the same problem, but instead of “hacking” or emulating command line:

Found this answer here.

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

回答 5

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

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

You can do something like this, to handle the host key checking as well

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

回答 6

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

fabric could be used to upload files vis 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()

回答 7

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

您只需要安装vassal并执行

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

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

You can use the vassal package, which is exactly designed for this.

All you need is to install vassal and do

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

Also, it will save you authenticate credential and don’t need to type them again and again.


回答 8

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

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

然后在python中:

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

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

I used sshfs to mount the remote directory via ssh, and shutil to copy the files:

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

Then in python:

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

This method has the advantage that you can stream data over if you are generating data rather than caching locally and sending a single large file.


回答 9

如果您不想使用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!')

Try this if you wan’t to use SSL certificates:

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

回答 10

使用外部资源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')

Using the external resource 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')

回答 11

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)

Calling scp command via subprocess doesn’t allow to receive the progress report inside the script. pexpect could be used to extract that info:

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

See python copy file in local network (linux -> linux)


回答 12

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

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

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

A very simple approach is the following:

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

No python library are required (only os), and it works, however using this method relies on another ssh client to be installed. This could result in undesired behavior if ran on another system.


回答 13

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

import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)

Kind of hacky, but the following should work :)

import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)

如何在python中进行scp?

问题:如何在python中进行scp?

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

What’s the most pythonic way to scp a file in Python? The only route I’m aware of is

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

which is a hack, and which doesn’t work outside Linux-like systems, and which needs help from the Pexpect module to avoid password prompts unless you already have passwordless SSH set up to the remote host.

I’m aware of Twisted’s conch, but I’d prefer to avoid implementing scp myself via low-level ssh modules.

I’m aware of paramiko, a Python module that supports SSH and SFTP; but it doesn’t support SCP.

Background: I’m connecting to a router which doesn’t support SFTP but does support SSH/SCP, so SFTP isn’t an option.

EDIT: This is a duplicate of How to copy a file to a remote server in Python using SCP or SSH?. However, that question doesn’t give an scp-specific answer that deals with keys from within Python. I’m hoping for a way to run code kind of like

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

回答 0

尝试使用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代码

Try the Python scp module for Paramiko. It’s very easy to use. See the following example:

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

Then call scp.get() or scp.put() to do SCP operations.

(SCPClient code)


回答 1

您可能对尝试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')

You might be interested in trying Pexpect (source code). This would allow you to deal with interactive prompts for your password.

Here’s a snip of example usage (for ftp) from the main website:

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

回答 2

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

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

scp.py for paramiko

You could also check out paramiko. There’s no scp module (yet), but it fully supports sftp.

[EDIT] Sorry, missed the line where you mentioned paramiko. The following module is simply an implementation of the scp protocol for paramiko. If you don’t want to use paramiko or conch (the only ssh implementations I know of for python), you could rework this to run over a regular ssh session using pipes.

scp.py for paramiko


回答 3

找不到直接的答案,并且此“ 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')

Couldn’t find a straight answer, and this “scp.Client” module doesn’t exist. Instead, this suits me:

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

回答 4

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

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

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


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

if you install putty on win32 you get an pscp (putty scp).

so you can use the os.system hack on win32 too.

(and you can use the putty-agent for key-managment)


sorry it is only a hack (but you can wrap it in a python class)


回答 5

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

from subprocess import call

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

You can use the package subprocess and the command call to use the scp command from the shell.

from subprocess import call

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

回答 6

到目前为止,最好的解决方案可能是 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)

As of today, the best solution is probably 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)

回答 7

看一下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/')

Have a look at 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/')

回答 8

自问这个问题以来已经有一段时间了,与此同时,另一个可以处理此问题的库也出现了:您可以使用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)

It has been quite a while since this question was asked, and in the meantime, another library that can handle this has cropped up: You can use the copy function included in the Plumbum library:

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)

回答 9

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

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

If you are on *nix you can use sshpass

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

回答 10

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

回答 11

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

Hmmm, perhaps another option would be to use something like sshfs (there an sshfs for Mac too). Once your router is mounted you can just copy the files outright. I’m not sure if that works for your particular application but it’s a nice solution to keep handy.


回答 12

不久前,我整理了一个依赖于paramiko的python SCP复制脚本。它包含用于处理与私钥或SSH密钥代理的连接以及后备密码验证的代码。

http://code.activestate.com/recipes/576810-copy-files-over-ssh-using-paramiko/

I while ago I put together a python SCP copy script that depends on paramiko. It includes code to handle connections with a private key or SSH key agent with a fallback to password authentication.

http://code.activestate.com/recipes/576810-copy-files-over-ssh-using-paramiko/


回答 13

我认为您可以轻松下载任何模块来实施scp,但是您可能会发现这很有用:http : //www.ibm.com/developerworks/linux/library/l-twist4.html

I don’t think there’s any one module that you can easily download to implement scp, however you might find this helpful: http://www.ibm.com/developerworks/linux/library/l-twist4.html