Jak scp w Pythonie?

158

Jaki jest najbardziej pythonowy sposób scp pliku w Pythonie? Jedyna trasa, jaką znam, to

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

co jest hackem i które nie działa poza systemami podobnymi do Linuksa i które wymaga pomocy modułu Pexpect, aby uniknąć monitów o hasło, chyba że masz już skonfigurowane SSH bez hasła na zdalnym hoście.

Jestem świadomy istnienia Twisted conch, ale wolałbym uniknąć samodzielnego wdrażania scp za pomocą niskopoziomowych modułów ssh.

Jestem świadomy paramiko, moduł Pythona, który obsługuje SSH i SFTP; ale nie obsługuje SCP.

Tło: Łączę się z routerem, który nie obsługuje SFTP, ale obsługuje SSH / SCP, więc SFTP nie jest opcją.

EDYCJA : To jest duplikat Jak skopiować plik na zdalny serwer w Pythonie przy użyciu SCP lub SSH? . Jednak to pytanie nie daje odpowiedzi specyficznej dla SCP, która dotyczy kluczy z Pythona. Mam nadzieję na sposób na uruchomienie kodu

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')
Michael Gundlach
źródło

Odpowiedzi:

106

Wypróbuj moduł scp Pythona dla Paramiko . Jest bardzo łatwy w użyciu. Zobacz poniższy przykład:

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

Następnie zadzwoń scp.get()lub scp.put()wykonaj operacje SCP.

( Kod klienta SCP )

Tom Shen
źródło
3
Tyle że ten moduł w rzeczywistości nie używa paramiko - jest to dużo kodu, który ostatecznie scpkończy się wywołaniem scpwiersza poleceń, który działa tylko na * nix.
Nick Bastin,
8
Zgoda, to, co widzisz w kodzie źródłowym, to zdalne wywołanie scp -tlub scp -f(tryby „do” i „z”, które istnieją dla tego celu po stronie serwera). W zasadzie tak działa scp - opiera się na innej kopii scp istniejącej na serwerze. Ten artykuł wyjaśnia to bardzo dobrze: blogs.oracle.com/janp/entry/how_the_scp_protocol_works
laher
8
To rozwiązanie zadziałało w przypadku mnie z dwoma zastrzeżeniami: 1. oto instrukcje importu, których użyłem: import paramiko from scp import SCPClient2. Oto przykład polecenia scp.get ():scp.get(r'/nfs_home/appers/xxxx/test2.txt', r'C:\Users\xxxx\Desktop\MR_Test')
Chris Nielsen
To nie tylko działa świetnie, ale także wykorzystuje klucze SSH, jeśli istnieją.
Marcel Wilson
Zauważ, że możesz również zainstalować tę bibliotekę z PyPI: pip install scpw wersji 0.10.2 w chwili pisania tego tekstu.
Andrew B.
15

Możesz być zainteresowany wypróbowaniem Pexpect ( kod źródłowy ). Umożliwiłoby to obsługę interaktywnych monitów o podanie hasła.

Oto fragment przykładowego użycia (dla ftp) z głównej strony internetowej:

# 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 ('[email protected]')
child.expect ('ftp> ')
child.sendline ('cd pub')
child.expect('ftp> ')
child.sendline ('get ls-lR.gz')
child.expect('ftp> ')
child.sendline ('bye')
Pat Notz
źródło
11

Możesz też sprawdzić paramiko . Nie ma (jeszcze) modułu scp, ale w pełni obsługuje sftp.

[EDYCJA] Przepraszam, przegapiłem linię, w której wspomniałeś o paramiko. Poniższy moduł jest po prostu implementacją protokołu scp dla paramiko. Jeśli nie chcesz używać paramiko lub conch (jedynych znanych mi implementacji ssh dla Pythona), możesz przerobić to tak, aby uruchamiało się przez zwykłą sesję ssh przy użyciu potoków.

scp.py dla paramiko

JimB
źródło
Czy chcesz powiedzieć, że dołączone rozwiązanie bezpiecznie przeniesie pliki na dowolną maszynę z uruchomionym sshd, nawet jeśli nie jest na niej uruchomiona sftpd? Dokładnie tego szukam, ale nie mogę powiedzieć z twojego komentarza, czy to po prostu otula sftp fasadą podobną do scp.
Michael Gundlach,
2
Spowoduje to przesłanie plików na dowolną maszynę z uruchomionym sshd, która ma scp w PATH (scp nie jest częścią specyfikacji ssh, ale jest dość wszechobecny). To wywołuje "scp -t" na serwerze i używa protokołu scp do przesyłania plików, który nie ma nic wspólnego z sftp.
JimB,
1
Zwróć uwagę, że jako modelu użyłem implementacji scp przez openssh, więc nie ma gwarancji, że zadziała z innymi wersjami. Niektóre wersje sshd mogą również używać protokołu scp2, który jest w zasadzie taki sam jak sftp.
JimB,
Czy mógłbyś zobaczyć to pytanie: stackoverflow.com/questions/20111242/… Zastanawiam się, czy paramiko używa podprocesu, czy czegoś innego, jak gniazda. Mam problemy z pamięcią podczas używania subprocess.check_output ('ssh [email protected] "cat / data / file *") z powodu problemów z klonowaniem / rozwidleniem i zastanawiam się, czy paramiko będzie mieć te same problemy, czy nie?
Paweł
1
@Paul: paramiko nie będzie mieć tych samych problemów, ponieważ nie używa podprocesu.
JimB,
9

Nie można znaleźć prostej odpowiedzi, a ten moduł „scp.Client” nie istnieje. Zamiast tego mi odpowiada:

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')
Maviles
źródło
6

jeśli zainstalujesz putty na win32, otrzymasz pscp (putty scp).

więc możesz użyć hackowania os.system również na win32.

(i możesz użyć agenta szpachlówki do zarządzania kluczami)


przepraszam, to tylko hack (ale można to opakować w klasę Pythona)

Blauohr
źródło
Jedynym powodem, dla którego działa 'nix one, jest to, że na ścieżce znajduje się SCP; jak wskazuje Blauohr, nie jest to zbyt trudne do naprawienia. +1
ojrac
6

Możesz użyć podprocesu pakietu i wywołania polecenia, aby użyć polecenia scp z powłoki.

from subprocess import call

cmd = "scp user1@host1:files user2@host2:files"
call(cmd.split(" "))
user7529863
źródło
2
Czym różni się to zasadniczo od przypadku podstawowego wspomnianego przez pytającego? Tak, podproces jest lepszy niż os.system, ale w obu przypadkach nie działa poza systemami podobnymi do linuxa, ma problemy z pytaniami o hasło itp.
Foon
5

Na dzień dzisiejszy prawdopodobnie najlepszym rozwiązaniem jest 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)
Loïc
źródło
1
Cześć. Twierdzisz, że AsyncSSH jest najlepszy, ale czy możesz złożyć oświadczenie lub 2 na poparcie tego twierdzenia? Może odróżnij to od scp () lub jak to miało znaczenie dla twojego przypadku użycia. (To powiedziawszy, jest znacznie mniej kodu - przynajmniej w twoim przykładzie)
Scott Prive,
2
@Crossfit_and_Beer witaj. Powiedziałem więc „chyba najlepsze”, ocenię każdemu :) Nie mam teraz zbyt wiele czasu na porównanie AsyncSSH z innymi bibliotekami. Ale bardzo szybko: jest prosty w użyciu, asynchroniczny, utrzymywany, a konserwator jest bardzo miły i pomocny, jest całkiem kompletny i bardzo dobrze udokumentowany.
Loïc
5

Spójrz na 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/')
user443854
źródło
2
Możesz być bardziej dokładny?
Nick T
4

Minęło sporo czasu, odkąd zadano to pytanie, a tymczasem pojawiła się kolejna biblioteka, która może sobie z tym poradzić: Możesz użyć funkcji kopiowania zawartej w bibliotece 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)
PMO
źródło
Jak mogę wprowadzić hasło do klucza prywatnego w Plumbum? p
ekta
@ekta: Zostaniesz poproszony o to w wierszu poleceń. Jeśli chcesz udostępnić go z własnego kodu, nie sądzę, aby API Plumbum to obsługiwało. Ale plumbum SshMachinema dostęp do ssh-agent, więc jeśli chcesz po prostu uniknąć wpisywania go za każdym razem, to jest jeden ze sposobów, aby to zrobić. Możesz również zgłosić prośbę o dodanie funkcji na stronie github plumbum.
pmos
2

Jeśli korzystasz z * nix, możesz użyć sshpass

sshpass -p password scp -o User=username -o StrictHostKeyChecking=no src dst:/path
user178047
źródło
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()
shrikkanth roxor
źródło
Pomogłoby, gdybyś dodał komentarz do swojej odpowiedzi wyjaśniający, dlaczego Twoje rozwiązanie jest dobre i pomógłby czytelnikom porównać je z innymi dostępnymi rozwiązaniami. Samo wysłanie kodu nie zawsze pomaga uczniowi wiedzieć, jak rozpoznać lepsze rozwiązania.
Brian Tompsett - 汤 莱恩
To jest SFTP, a nie SCP.
Martin Prikryl
1

Hmmm, być może inną opcją byłoby użycie czegoś takiego jak sshfs (jest też sshfs dla Maca). Po zamontowaniu routera możesz po prostu skopiować pliki. Nie jestem pewien, czy to działa w twojej konkretnej aplikacji, ale jest to fajne rozwiązanie, które warto mieć pod ręką.

Pat Notz
źródło
1

Jakiś czas temu stworzyłem skrypt kopiujący SCP w Pythonie, który zależy od paramiko. Zawiera kod do obsługi połączeń z kluczem prywatnym lub agentem klucza SSH z możliwością powrotu do uwierzytelniania hasłem.

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

ccpizza
źródło
Dzięki za link i za poświęcenie czasu na opublikowanie dobrego przykładu użycia paramiko.
Peter M. - oznacza Monikę