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

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 )


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

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


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 blah@blah.com "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')

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)


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

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)

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

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)

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

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

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ą.



Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.