Как скопировать файл на удаленный сервер на Python с помощью SCP или SSH?

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

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

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
110
0
289 097
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

Ответ принят как подходящий

Вы можете вызвать команду bash scp (она копирует файлы поверх SSH) с помощью subprocess.run:

import subprocess
subprocess.run(["scp", FILE, "USER@SERVER:PATH"])
#e.g. subprocess.run(["scp", "foo.bar", "[email protected]:/path/to/foo.bar"])

Если вы создаете файл, который хотите отправить, в той же программе Python, вам нужно вызвать команду subprocess.run за пределами блока with, который вы используете для открытия файла (или сначала вызовите .close() для файла, если вы без использования блока with), поэтому вы знаете, что он сбрасывается на диск из Python.

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

@CharlesDuffy: subprocess.check_call(['scp', srcfile, dest]) можно использовать с Python 2.5 вместо rc = Popen(..).wait(); if rc != 0: raise ..

jfs 28.03.2014 14:37

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

orezvani 16.11.2014 20:58

Вроде хаки, но должно работать следующее :)

import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" [email protected]:"+serverPath)

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

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 для этого уже.

Использование subprocess.Popen - это правильная вещь. Передача ему строки, а не массива (и использование shell = True) является неправильным, поскольку это означает, что имена файлов с пробелами работают неправильно.

Charles Duffy 15.10.2008 16:07

вместо sts = os.waitpid (p.pid, 0) мы можем использовать sts = p.wait ().

db42 18.12.2011 16:19

есть ли способ без выполнения с прямым API Python для этого протокола?

lpapp 10.12.2013 13:48

Вместо него можно использовать subprocess.check_call(['scp', myfile, destination]), начиная с Python 2.5 (2006 г.)

jfs 28.03.2014 15:10

@ J.F.Sebastian: да, я проверял это в декабре. По крайней мере, мои голоса подтверждают это. :) Спасибо за продолжение.

lpapp 28.03.2014 15:45

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

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

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

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

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

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

Любопытно: как обстоят дела с rsync и scp?

Alok 16.09.2008 06:16

Чтобы сделать это в Python (т.е. не обертывать scp через subprocess.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()

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

paramiko также имеет красивую функцию sftp.put (self, localpath, remotepath, callback = None), поэтому вам не нужно открывать запись и закрывать каждый файл.

Jim Carroll 01.10.2009 17:28

Замечу, что SFTP - это не то же самое, что SCP.

Drahkar 15.09.2012 03:47

@Drahkar вопрос просит пересылку файла по SSH. Вот что это значит.

Tony Meyer 21.09.2012 01:50

Примечание: чтобы сделать эту работу нестандартной, добавьте ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) после создания экземпляра ssh.

doda 09.10.2012 18:49

@doda, только если вы не хотите использовать файл known_hosts

Tom 20.09.2013 03:32

Есть идеи для решения на основе "scp"? SFTP не работает с нашим сервером.

lpapp 10.12.2013 13:48

Ребята, безопасно ли использовать пароль сервера? есть ли возможность использовать закрытый ключ?

Aysennoussi 31.12.2014 19:09

Конечно, можно использовать закрытый ключ: gist.github.com/batok/2352501

Joel B 24.04.2015 19:38

Интересно, что этот ответ набрал почти в три раза больше голосов, чем принятый.

WinEunuuchs2Unix 10.08.2020 03:49

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

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

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

Примечание. Это зависит от пакета scp. По состоянию на декабрь 2017 г. последнее обновление этого пакета было в 2015 г., а номер версии - 0.1.x. Не уверен, что хочу добавить эту зависимость.

Chuck 10.12.2017 04:24

Итак, сейчас 2018 год, и в мае они выпустили версию 0.11.0. Итак, кажется, SCPClient все-таки не мертв?

Adriano_Pinaffo 02.08.2018 17:26

Использование внешнего ресурса 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')

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

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

Библиотеки python не требуются (только os), и она работает, однако использование этого метода требует установки другого клиента ssh. Это может привести к нежелательному поведению при запуске в другой системе.

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

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

Затем в питоне:

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

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

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

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

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

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

Попробуйте это, если вы не хотите использовать сертификаты 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 -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")

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