Як зробити scp в Python?


158

Який самий пітонічний спосіб скроїти файл у Python? Єдиний маршрут, про який я знаю, - це

os.system('scp "%s" "%s:%s"' % (localfile, remotehost, remotefile) )

що є злом, і який не працює за межами систем, схожих на Linux, і яким потрібна допомога модуля Pexpect, щоб уникнути підказок пароля, якщо ви вже не встановили безвідмовний SSH для віддаленого хоста.

Мені відомо про Twisted conch, але я вважаю за краще уникати впровадження scp через модулі ssh низького рівня.

Мені відомо paramiko, модуль Python, який підтримує SSH та SFTP; але він не підтримує SCP.

Передумови: я підключаюся до маршрутизатора, який не підтримує SFTP, але підтримує SSH / SCP, тому SFTP не є варіантом.

EDIT : Це дублікат того, як скопіювати файл на віддалений сервер Python за допомогою SCP або SSH? . Однак це питання не дає конкретного відповіді, який стосується ключів з Python. Я сподіваюся, що спосіб запускати подібний код

import scp

client = scp.Client(host=host, user=user, keyfile=keyfile)
# or
client = scp.Client(host=host, user=user)
client.use_system_keys()
# or
client = scp.Client(host=host, user=user, password=password)

# and then
client.transfer('/etc/local/filename', '/etc/remote/filename')

Відповіді:


106

Спробуйте модуль scp Python для Paramiko . Це дуже просто у використанні. Дивіться наступний приклад:

import paramiko
from scp import SCPClient

def createSSHClient(server, port, user, password):
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(server, port, user, password)
    return client

ssh = createSSHClient(server, port, user, password)
scp = SCPClient(ssh.get_transport())

Потім зателефонуйте scp.get()або scp.put()виконайте операції SCP.

( Код SCPClient )


3
За винятком того, що модуль насправді не використовує параміко - це дуже багато коду, який врешті- scpрешт і справді викликає scpкомандний рядок, який працює лише на * nix.
Нік Бастін

8
Погоджено, що ви бачите у вихідному коді віддалений виклик до scp -tабо scp -f("до" та "з" режимів, які існують для цієї серверної мети). Це, по суті, як працює scp - він спирається на іншу копію scp, існуючу на сервері. Ця стаття це дуже добре пояснює: blogs.oracle.com/janp/entry/how_the_scp_protocol_works
laher

8
Це рішення працювало для мене з двома застереженнями: 1. Це імпорт імпорту, який я використав: import paramiko from scp import SCPClient2. Ось приклад команди scp.get ():scp.get(r'/nfs_home/appers/xxxx/test2.txt', r'C:\Users\xxxx\Desktop\MR_Test')
Chris Nielsen

Це не тільки чудово працює, але також використовує SSH-ключі, якщо вони існують.
Marcel Wilson

Зауважте, що ви також можете встановити цю лібу з PyPI: pip install scpу версії 0.10.2 станом на цей текст.
Ендрю Б.

15

Можливо, вам буде цікаво спробувати Pexpect ( вихідний код ). Це дозволить вам обробляти інтерактивні підказки щодо пароля.

Ось фрагмент використання прикладу (для ftp) з головного веб-сайту:

# This connects to the openbsd ftp site and
# downloads the recursive directory listing.
import pexpect
child = pexpect.spawn ('ftp ftp.openbsd.org')
child.expect ('Name .*: ')
child.sendline ('anonymous')
child.expect ('Password:')
child.sendline ('noah@example.com')
child.expect ('ftp> ')
child.sendline ('cd pub')
child.expect('ftp> ')
child.sendline ('get ls-lR.gz')
child.expect('ftp> ')
child.sendline ('bye')

11

Ви також можете перевірити параміко . Немає модуля scp (поки що), але він повністю підтримує sftp.

[EDIT] Вибачте, пропустив рядок, де ви згадали параміко. Наступний модуль - це просто реалізація протоколу scp для paramiko. Якщо ви не хочете використовувати paramiko або conch (єдині ssh реалізації, які я знаю для python), ви можете переробити це для запуску звичайного сеансу ssh за допомогою труб.

scp.py для параміко


Ви хочете сказати, що додане рішення надійно передаватиме файли на будь-яку машину, на якій працює sshd, навіть якщо це не працює sftpd? Це саме те, що я шукаю, але я не можу сказати з вашого коментаря, чи це просто загортає sftp у фасад, схожий на scp.
Майкл Гундлах

2
Це передасть файли на будь-якій машині, що працює з sshd, яка має scp в PATH (scp не є частиною специфікації ssh, але це досить всюди). Це викликає "scp -t" на сервері і використовує протокол scp для передачі файлів, що не має нічого спільного з sftp.
JimB

1
Зауважте, що в якості моделі я використовував opensh реалізацію scp, тому це не гарантовано для роботи з іншими версіями. Деякі версії sshd можуть також використовувати протокол scp2, який в основному такий же, як sftp.
JimB

Чи можете ви побачити це питання: stackoverflow.com/questions/20111242/… Мені цікаво, чи параміко використовує підпроцес чи щось інше, як сокети. У мене виникають проблеми з пам'яттю за допомогою subprocess.check_output ('ssh blah@blah.com "cat / data / file *") через проблеми з клонуванням / виделкою, і мені цікаво, чи буде у paramiko ті самі проблеми чи ні?
Пол

1
@Paul: paramiko не матиме однакових проблем, оскільки він не використовує підпроцес.
JimB

9

Не вдалося знайти прямої відповіді, і цей модуль "scp.Client" не існує. Натомість це мені підходить:

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

якщо встановити шпаклівку на win32, ви отримаєте pscp (putty scp).

тож ви можете використовувати хак os.system і на win32.

(і ви можете використовувати putty-agent для керування ключами)


вибачте, що це лише хак (але ви можете зафіксувати його в класі python)


Єдина причина, по якій працює «nix one», це те, що ти маєш SCP на шляху; як зазначає Блауор, це не надто важко виправити. +1
ojrac

6

Ви можете використовувати підпроцес пакета та виклик команд, щоб використовувати команду scp з оболонки.

from subprocess import call

cmd = "scp user1@host1:files user2@host2:files"
call(cmd.split(" "))

2
Чим це суттєво відрізняється від базового випадку, згаданого запитувачем? Так, підпроцес краще, ніж os.system, але в обох випадках він не працює за межами систем, схожих на Linux, має проблеми із підказками пароля тощо
Foon

5

На сьогоднішній день, мабуть, найкраще рішення AsyncSSH

https://asyncssh.readthedocs.io/en/latest/#scp-client

async with asyncssh.connect('host.tld') as conn:
    await asyncssh.scp((conn, 'example.txt'), '.', recurse=True)

1
Привіт. Ви заявляєте, що AsyncSSH є найкращим, але чи можете ви зробити заяву або 2, щоб підтримати цю вимогу? Можливо, відмежуйте його від scp (), або як воно має значення для вашого випадку використання. (Це говорило, що це набагато менше коду - як мінімум у вашому прикладі)
Скотт

2
@Crossfit_and_Beer привіт. Тому я сказав "напевно, найкраще", я дам рішення будь-кому :) Я зараз не так багато часу для порівняння AsyncSSH з іншими бібліотеками. Але дуже швидко: він простий у використанні, він асинхронний, він підтримується, а maintener дуже приємний і корисний, він досить повно і дуже добре задокументований.
Loïc

5

Погляньте на fabric.transfer .

from fabric import Connection

with Connection(host="hostname", 
                user="admin", 
                connect_kwargs={"key_filename": "/home/myuser/.ssh/private.key"}
               ) as c:
    c.get('/foo/bar/file.txt', '/tmp/')

2
Чи можете ви бути більш конкретними?
Нік Т

4

Минуло дуже багато часу з того часу, як було задано це запитання, а тим часом з'явилася ще одна бібліотека, яка може впоратися з цим: Ви можете використовувати функцію копіювання, включену в бібліотеку Plumbum :

import plumbum
r = plumbum.machines.SshMachine("example.net")
   # this will use your ssh config as `ssh` from shell
   # depending on your config, you might also need additional
   # params, eg: `user="username", keyfile=".ssh/some_key"`
fro = plumbum.local.path("some_file")
to = r.path("/path/to/destination/")
plumbum.path.utils.copy(fro, to)

Як я можу ввести парольну фразу для приватного ключа за допомогою Plumbum? p
ekta

@ekta: Вам буде запропоновано про це в командному рядку. Якщо ви хочете надати його з власного коду, я не думаю, що API PLumbum це підтримує. Але до сливових SshMachineє доступ ssh-agent, тож якщо ви просто хочете уникати вводити його щоразу, це один із способів. Ви також можете подати запит на функцію на сторінці github plumbum.
pmos


2
import paramiko

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

client.connect('<IP Address>', username='<User Name>',password='' ,key_filename='<.PEM File path')

#Setup sftp connection and transmit this script 
print ("copying")

sftp = client.open_sftp() 
sftp.put(<Source>, <Destination>)


sftp.close()

Було б корисно, якщо ви додасте коментар до своєї відповіді, пояснюючи, чому ваше рішення є хорошим, і допоможете читачам порівняти його з іншими доступними рішеннями. Просто розміщення коду не завжди допомагає школяреві знати, як розпізнати кращі рішення.
Брайан Томпсетт - 汤 莱恩

Це SFTP, а не SCP.
Мартін Прикрил

1

Гммм, можливо, іншим варіантом було б використовувати щось на кшталт sshfs (є також sshfs для Mac). Після встановлення вашого маршрутизатора ви можете просто скопіювати файли прямо. Я не впевнений, що це працює для вашої конкретної програми, але це приємне рішення, щоб бути в нагоді.


1

Я в той час як склав сценарій копіювання пітона SCP, який залежить від paramiko. Він включає код для обробки з'єднань з приватним ключем або агентом SSH-ключа з резервним до автентифікації паролем.

http://code.activestate.com/recipes/576810-copy-files-over-ssh-using-paramiko/


Дякуємо за посилання та за те, що знайшли час, щоб опублікувати хороший приклад використання параміко.
Пітер М. - виступає за Моніку

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.