问题:如何使用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)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。