Come scp in Python?


158

Qual è il modo più pitonico per scp un file in Python? L'unica via di cui sono a conoscenza è

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

che è un hack e che non funziona al di fuori dei sistemi simili a Linux e che necessita dell'aiuto del modulo Pexpect per evitare le richieste di password a meno che non si abbia già SSH senza password configurato per l'host remoto.

Sono a conoscenza di Twisted conch, ma preferirei evitare di implementare scp da solo tramite moduli ssh di basso livello.

Sono a conoscenza di paramikoun modulo Python che supporta SSH e SFTP; ma non supporta SCP.

Background: mi sto collegando a un router che non supporta SFTP ma supporta SSH / SCP, quindi SFTP non è un'opzione.

EDIT : questo è un duplicato di Come copiare un file su un server remoto in Python usando SCP o SSH? . Tuttavia , quella domanda non fornisce una risposta specifica per scp che si occupa delle chiavi all'interno di Python. Spero in un modo per eseguire il codice in qualche modo

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

Risposte:


106

Prova il modulo scp Python per Paramiko . È molto facile da usare. Vedi il seguente esempio:

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

Quindi chiamare scp.get()o scp.put()eseguire operazioni SCP.

( Codice SCPClient )


3
Tranne che quel modulo in realtà non usa paramiko - è un sacco di codice che alla fine scpfinisce per chiamare effettivamente la scpriga di comando che funziona solo su * nix.
Nick Bastin,

8
D'accordo, quello che vedi nel codice sorgente è la chiamata remota verso scp -to scp -f(modalità 'a' e 'da', che esistono per questo scopo lato server). Questo è essenzialmente il modo in cui scp funziona: si basa su un'altra copia di scp esistente sul server. Questo articolo lo spiega molto bene: blogs.oracle.com/janp/entry/how_the_scp_protocol_works
laher

8
Questa soluzione ha funzionato per me con due avvertenze: 1. queste sono le dichiarazioni di importazione che ho usato: import paramiko from scp import SCPClient2. Ecco un esempio del comando scp.get ():scp.get(r'/nfs_home/appers/xxxx/test2.txt', r'C:\Users\xxxx\Desktop\MR_Test')
Chris Nielsen

Questo non solo funziona alla grande, ma sfrutta anche le chiavi SSH se esistono.
Marcel Wilson,

Nota che puoi installare questa lib anche da PyPI:, pip install scpalla versione 0.10.2 al momento della stesura di questo documento.
Andrew B.

15

Potresti essere interessato a provare Pexpect ( codice sorgente ). Ciò ti consentirebbe di gestire le richieste interattive per la tua password.

Ecco un esempio di utilizzo di esempio (per ftp) dal sito Web principale:

# 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

Puoi anche dare un'occhiata a Paramiko . Non esiste un modulo scp (ancora), ma supporta pienamente sftp.

[EDIT] Siamo spiacenti, ho perso la riga in cui hai menzionato paramiko. Il seguente modulo è semplicemente un'implementazione del protocollo scp per paramiko. Se non vuoi usare paramiko o conch (le uniche implementazioni ssh che conosco per Python), potresti rielaborarlo per eseguire una normale sessione ssh usando le pipe.

scp.py per paramiko


Stai dicendo che la soluzione allegata trasferirà i file in modo sicuro su qualsiasi macchina che esegue sshd, anche se non sta eseguendo sftpd? Questo è esattamente quello che sto cercando, ma non posso dire dal tuo commento se questo avvolge semplicemente sftp in una facciata simile a SCP.
Michael Gundlach,

2
Questo trasferirà i file con qualsiasi macchina che esegue sshd, che ha scp nel PATH (scp non fa parte delle specifiche ssh, ma è abbastanza onnipresente). Questo invoca "scp -t" sul server e usa il protocollo scp per trasferire file, che non ha nulla a che fare con sftp.
JimB,

1
Nota che ho usato l'implementazione di openssh di scp come modello, quindi non è garantito che funzioni con altre versioni. Alcune versioni di sshd possono anche usare il protocollo scp2, che è sostanzialmente lo stesso di sftp.
JimB,

Potresti vedere questa domanda: stackoverflow.com/questions/20111242/… Mi chiedo se paramiko utilizza sottoprocesso o comething come socket. Sto riscontrando problemi di memoria utilizzando subprocess.check_output ('ssh blah@blah.com "cat / data / file *") a causa di problemi di clone / fork e mi chiedo se paramiko avrà gli stessi problemi o no?
Paul,

1
@Paul: paramiko non avrà gli stessi problemi, poiché non utilizza un sottoprocesso.
JimB,

9

Impossibile trovare una risposta diretta e questo modulo "scp.Client" non esiste. Invece, questo mi va bene:

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

se installi putty su win32 otterrai un pscp (putty scp).

così puoi usare l'hack di os.system anche su win32.

(e puoi usare lo stucco-agente per la gestione delle chiavi)


scusa è solo un trucco (ma puoi avvolgerlo in una classe Python)


L'unica ragione per cui 'nix one funziona è che hai SCP sul percorso; come sottolinea Blauohr, non è difficile da risolvere. +1
ojrac,

6

È possibile utilizzare il sottoprocesso del pacchetto e la chiamata al comando per utilizzare il comando scp dalla shell.

from subprocess import call

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

2
In che modo ciò differisce sostanzialmente dal caso di base menzionato dall'interrogante? Sì, il sottoprocesso è migliore di os.system, ma in entrambi i casi non funziona al di fuori dei sistemi simili a Linux, ha problemi con le richieste di password, ecc.
Foon

5

Ad oggi, la soluzione migliore è probabilmente 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
Ciao. Hai presentato una richiesta in merito al fatto che AsyncSSH è il migliore, ma potresti fare una dichiarazione o 2 a supporto di tale richiesta? Forse differenziarlo da scp () o come è importante per il tuo caso d'uso. (Detto questo, è di gran lunga meno codice - almeno nel tuo esempio)
Scott Prive

2
@Crossfit_and_Beer ciao. Quindi ho detto "probabilmente il migliore", lascerò il giudizio a chiunque :) Non ho molto tempo adesso per confrontare AsyncSSH con altre librerie. Ma molto rapidamente: è semplice da usare, è asincrono, è mantenuto e il manutentore è molto gentile e disponibile, è abbastanza completo e ben documentato.
Loïc,

5

Dai un'occhiata a 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
Potresti essere più specifico?
Nick T,

4

È passato un po 'di tempo da quando è stata posta questa domanda e, nel frattempo, è spuntata un'altra libreria in grado di gestirla: è possibile utilizzare la funzione di copia inclusa nella libreria 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)

Come posso inserire la passphrase per la chiave privata con Plumbum? p
ekta,

@ekta: ti verrà richiesto dalla riga di comando. Se si desidera fornirlo dal proprio codice, non credo che l'API di Plumbum lo supporti. Ma Plumbum SshMachineha accesso a ssh-agent, quindi se vuoi solo evitare di digitarlo ogni volta, è un modo per farlo. Puoi anche presentare una richiesta di funzione sulla pagina github di plumbum.
pmos,

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

Sarebbe utile se aggiungessi alcuni commenti alla tua risposta spiegando perché la tua soluzione è valida e aiutando i lettori a confrontarla con le altre soluzioni disponibili. La semplice pubblicazione di codice non sempre aiuta lo studente a riconoscere le soluzioni migliori.
Brian Tompsett -

Questo è SFTP, non SCP.
Martin Prikryl il

2

Se sei su * nix puoi usare sshpass

sshpass -p password scp -o User=username -o StrictHostKeyChecking=no src dst:/path

1

Hmmm, forse un'altra opzione sarebbe quella di usare qualcosa come sshfs (c'è un sshfs anche per Mac). Una volta montato il router, puoi semplicemente copiare i file in modo definitivo. Non sono sicuro che funzioni per la tua specifica applicazione ma è una buona soluzione da tenere a portata di mano.



Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.