繁体   English   中英

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

[英]How to copy a file to a remote server in Python using SCP or SSH?

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

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

要在 Python 中使用Paramiko库(即不通过 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()

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

您可以拨打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 程序中创建要发送的文件,则需要在用于打开文件的with块之外调用subprocess.run命令(或在文件上调用.close()首先,如果您没有使用with块),那么您就知道它是从 Python 刷新到磁盘的。

您需要预先生成(在源机器上)并安装(在目标机器上)一个 ssh 密钥,以便 scp 自动使用您的公共 ssh 密钥进行身份验证(换句话说,这样您的脚本就不会要求输入密码) .

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

有几种不同的方法可以解决这个问题:

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

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

注意:如果您计划通过 SSH 传输文件,它很难击败 rsync,尤其是如果替代方案是普通的旧 scp。

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

如果选择系统调用路径,Python 会提供一系列选项,例如 os.system 或 commands/subprocess 模块。 如果使用版本 2.4+,我会使用 subprocess 模块。

遇到了同样的问题,但不是“黑客”或模拟命令行:

在这里找到了这个答案。

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

你也可以做这样的事情来处理主机密钥检查

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

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

您可以使用专为此设计的 vassal 包。

您只需要安装 vassal 并执行

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

此外,它会保存您的身份验证凭据,而无需一次又一次地键入它们。

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

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

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

然后在python中:

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

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

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

通过子进程调用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)

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

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

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

有点hacky,但以下应该有效:)

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM