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

StackOverflow https://stackoverflow.com/questions/68335

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

Это было полезно?

Решение

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

Сначала вам нужно ".close()" запустить файл, чтобы вы знали, что он сброшен на диск из Python.

import os
os.system("scp FILE USER@SERVER:PATH")
#e.g. os.system("scp foo.bar joe@srvr.net:/path/to/foo.bar")

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

пример ssh-кейгена

Другие советы

Чтобы сделать это на Python (т. е.не оборачивать scp через подпроцесс.Popen или аналогичный) с помощью Парамико библиотека, вы бы сделали что-то вроде этого:

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

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

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

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. используйте библиотеку Python, которая предоставляет возможности SSH (например - Парамико или Витая Раковина)

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

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

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

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

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

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

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

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

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

Зовущий 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)

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, и шутил чтобы скопировать файлы:

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

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

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

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

Немного халтурно, но следующее должно сработать :)

import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top