У мене на локальній машині є текстовий файл, що генерується щоденним сценарієм Python, запущеним у cron.
Я хотів би додати трохи коду, щоб цей файл надійно надсилався на мій сервер через SSH.
У мене на локальній машині є текстовий файл, що генерується щоденним сценарієм Python, запущеним у cron.
Я хотів би додати трохи коду, щоб цей файл надійно надсилався на мій сервер через SSH.
Відповіді:
Ви можете викликати команду 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 (іншими словами, щоб ваш скрипт не запитував пароль) .
subprocess.check_call(['scp', srcfile, dest])
можна використовувати з Python 2.5 замістьrc = Popen(..).wait(); if rc != 0: raise ..
Щоб зробити це в 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()
(Ви, ймовірно, хочете мати справу з невідомими хостами, помилками, створення будь-яких необхідних каталогів тощо).
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
після інстанції ssh
.
Напевно, ви використовуєте модуль підпроцесу . Щось на зразок цього:
import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)
Де destination
, мабуть, форма user@remotehost:remotepath
. Завдяки @Charles Duffy за вказівку на слабкість моєї оригінальної відповіді, яка використовувала єдиний аргумент рядка для визначення операції scp shell=True
- що не оброблятиме пробіли у шляхах.
Документація модуля містить приклади перевірки помилок, які ви, можливо, захочете виконати разом із цією операцією.
Переконайтесь, що ви налаштували належні облікові дані, щоб ви могли виконувати без нагляду безпардовий пошук між машинами . Для цього вже є питання про stackoverflow .
subprocess.check_call(['scp', myfile, destination])
може бути використаний натомість з Python 2.5 (2006)
Існує кілька різних способів вирішити проблему:
У кожного підходу є свої примхи. Вам потрібно буде встановити 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')
Ви можете зробити щось подібне і для перевірки ключа хоста
import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")
fabric
може використовуватися для завантаження файлів 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()
Можна скористатися васальним пакетом, який саме призначений для цього.
Все, що вам потрібно, це встановити васал і зробити
from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()
Крім того, це дозволить вам зберегти автентифікацію облікових даних і не потрібно вводити їх знову і знову.
Я використовував 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!')
Використання зовнішнього ресурсу 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')
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-клієнт, який потрібно встановити. Це може призвести до небажаної поведінки, якщо працювати в іншій системі.