it-swarm-ru.tech

Как скопировать файл на удаленный сервер в Python, используя SCP или SSH?

У меня на локальном компьютере есть текстовый файл, который генерируется ежедневным скриптом Python, запускаемым в cron.

Я хотел бы добавить немного кода для безопасной отправки этого файла на мой сервер через SSH.

90
Alok

Если вы хотите простой подход, это должно работать.

Сначала вы захотите ".close ()" файл, чтобы вы знали, что он записан на диск из Python.

import os
os.system("scp FILE [email protected]:PATH")
#e.g. os.system("scp foo.bar [email protected]:/path/to/foo.bar")

Вам нужно сгенерировать (на исходном компьютере) и установить (на конечном компьютере) ключ ssh заранее, чтобы scp автоматически проходил аутентификацию с вашим открытым ключом ssh (другими словами, чтобы ваш скрипт не запрашивал пароль) , 

пример ssh-keygen

39
pdq

Чтобы сделать это в Python (то есть не оборачивая scp через subprocess.Popen или аналогичный) с библиотекой Paramiko , вы должны сделать что-то вроде этого:

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

(Возможно, вы захотите иметь дело с неизвестными хостами, ошибками, созданием любых необходимых каталогов и т.д.).

129
Tony Meyer

Возможно, вы бы использовали модуль подпроцесса . Что-то вроде этого:

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

Где destination, вероятно, имеет вид [email protected]:remotepath. Спасибо @ Charles Duffy за указание на слабость в моем первоначальном ответе, который использовал один строковый аргумент для указания операции scp Shell=True - которая не будет обрабатывать пробелы в путях.

Документация к модулю содержит примеры проверки ошибок, которые вы можете выполнить в связи с этой операцией.

Убедитесь, что вы установили правильные учетные данные, чтобы вы могли выполнить автоматическая scp без пароля между компьютерами . Существует вопрос stackoverflow для этого уже .

28
Blair Conrad

Есть несколько разных способов решения проблемы:

  1. Обтекание программ командной строки
  2. использовать библиотеку Python, которая предоставляет возможности SSH (например, - Paramiko или Twisted Conch )

У каждого подхода есть свои причуды. Вам нужно будет настроить ключи SSH для включения входа без пароля, если вы переносите системные команды, такие как «ssh», «scp» или «rsync». Вы можете встроить пароль в сценарий, используя Paramiko или какую-либо другую библиотеку, но вы можете столкнуться с недостатком документации, особенно если вы не знакомы с основами SSH-соединения (например, обмен ключами, агенты и т.д.). Вероятно, само собой разумеется, что ключи SSH почти всегда лучше, чем пароли для такого рода вещей.

ПРИМЕЧАНИЕ: rsync трудно победить, если вы планируете передавать файлы через SSH, особенно если альтернативой является старый scp.

Я использовал Paramiko, стараясь заменить системные вызовы, но обнаружил, что возвращаюсь к упакованным командам из-за их простоты использования и непосредственного знакомства. Вы можете быть другим. Я дал Кончу некоторое время назад, но это не обращалось ко мне.

При выборе пути к системному вызову Python предлагает массив опций, таких как os.system или модули command/subprocess. Я бы пошел с модулем подпроцесса, если использовать версию 2.4+.

10
Michael

Достигнута та же проблема, но вместо «взлома» или эмуляции командной строки:

Нашел этот ответ здесь .

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
Maviles

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

Вы можете использовать вассальную упаковку, которая предназначена именно для этого.

Все, что вам нужно, это установить вассала и сделать

from vassal.terminal import Terminal
Shell = Terminal(["scp [email protected]:/home/foo.txt foo_local.txt"])
Shell.run()

Кроме того, это сохранит ваши аутентификационные данные и не нужно будет вводить их снова и снова.

1
Shawn
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
michael

очень простой подход заключается в следующем: 

import os
os.system('sshpass -p "password" scp [email protected]:/path/to/file ./')

библиотека Python не требуется (только ОС), и это работает

0
Roberto Marzocchi

Попробуйте это, если вы не хотите использовать 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!')
0
JavDomGom

Я использовал sshfs для монтирования удаленного каталога через ssh и shutil для копирования файлов:

$ mkdir ~/sshmount
$ sshfs [email protected]:/path/to/remote/dst ~/sshmount

Тогда в питоне:

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

Преимущество этого метода заключается в том, что вы можете выполнять потоковую передачу данных, если вы генерируете данные, а не кэшируете локально и отправляете один большой файл.

0
Jonno_FTW

Вызов команды 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
jfs