it-swarm.it

Come copiare un file su un server remoto in Python usando SCP o SSH?

Ho un file di testo sul mio computer locale che viene generato da uno script giornaliero Python eseguito in cron. 

Vorrei aggiungere un po 'di codice per avere quel file inviato in modo sicuro al mio server tramite SSH.

85
Alok

Se vuoi un approccio semplice, dovrebbe funzionare.

Dovrai prima ".close ()" il file in modo da sapere che viene scaricato su disco da Python.

import os
os.system("scp FILE [email protected]:PATH")
#e.g. os.system("scp foo.bar [email protected]:/path/to/foo.bar")

È necessario generare (sul computer di origine) e installare (sul computer di destinazione) una chiave ssh in anticipo in modo che lo scp venga automaticamente autenticato con la propria chiave ssh pubblica (in altre parole, quindi lo script non richiede una password) . 

Esempio di ssh-keygen

39
pdq

Per fare ciò in Python (ovvero non eseguendo lo scp tramite subprocess.Popen o simili) con la libreria Paramiko , si dovrebbe fare qualcosa di simile a questo:

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

(Probabilmente vorrai gestire host sconosciuti, errori, creare qualsiasi directory necessaria e così via).

129
Tony Meyer

Probabilmente useresti il ​​modulo subprocess . Qualcosa come questo:

import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)

Dove destination è probabilmente della forma [email protected]:remotepath. Grazie a @ Charles Duffy per aver sottolineato la debolezza nella mia risposta originale, che utilizzava un argomento a stringa singola per specificare l'operazione scp Shell=True - che non gestiva gli spazi bianchi nei percorsi.

La documentazione del modulo contiene esempi di controllo degli errori che potresti voler eseguire insieme a questa operazione.

Assicurarsi di aver impostato le credenziali appropriate in modo da poter eseguire uno _ sct automatico, senza password tra le macchine . C'è già una domanda StackOverflow su questo .

28
Blair Conrad

Ci sono un paio di modi diversi per affrontare il problema:

  1. Avvolgi i programmi da riga di comando
  2. usa una libreria Python che fornisce funzionalità SSH (es. - Paramiko o Twisted Conch )

Ogni approccio ha le sue peculiarità. Sarà necessario impostare le chiavi SSH per abilitare gli accessi senza password se si stanno eseguendo comandi di sistema come "ssh", "scp" o "rsync". Puoi incorporare una password in uno script usando Paramiko o qualche altra libreria, ma potresti trovare frustrante la mancanza di documentazione, specialmente se non hai familiarità con le basi della connessione SSH (es. Scambi di chiavi, agenti, ecc.). Probabilmente è ovvio che le chiavi SSH sono quasi sempre un'idea migliore delle password per questo tipo di cose.

NOTA: è difficile da battere rsync se si prevede di trasferire file tramite SSH, specialmente se l'alternativa è semplice vecchia scp.

Ho usato Paramiko con un occhio verso la sostituzione delle chiamate di sistema, ma mi sono ritrovato ricondotto ai comandi avvolti a causa della loro facilità d'uso e immediata familiarità. Potresti essere diverso. Ho dato a Conch una volta, qualche tempo fa, ma non mi piaceva.

Se si opta per il percorso di chiamata di sistema, Python offre una serie di opzioni come os.system o i moduli di comandi/sottoprocessi. Vorrei andare con il modulo subprocess se si utilizza la versione 2.4+.

10
Michael

Raggiunto lo stesso problema, ma invece di "hacking" o emulazione della riga di comando:

Trovato questa risposta qui .

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
Maviles

È possibile utilizzare il pacchetto vassal, che è progettato esattamente per questo.

Tutto ciò che serve è installare Vassal e fare

from vassal.terminal import Terminal
Shell = Terminal(["scp [email protected]:/home/foo.txt foo_local.txt"])
Shell.run()

Inoltre, salverà le credenziali di autenticazione e non sarà necessario scriverle più e più volte.

1
Shawn
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')
0
michael

un approccio molto semplice è il seguente: 

import os
os.system('sshpass -p "password" scp [email protected]:/path/to/file ./')

non sono richieste librerie python (solo os) e funziona

0

Prova questo se non vuoi usare i certificati 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!')
0
JavDomGom

Ho usato sshfs per montare la directory remota tramite ssh, e shutil per copiare i file:

$ mkdir ~/sshmount
$ sshfs [email protected]:/path/to/remote/dst ~/sshmount

Quindi in python:

import shutil
shutil.copy('a.txt', '~/sshmount')

Questo metodo ha il vantaggio di poter eseguire lo streaming dei dati se si generano dati anziché la memorizzazione nella cache locale e l'invio di un singolo file di grandi dimensioni.

0
Jonno_FTW

Chiamare il comando scp tramite il sottoprocesso non consente di ricevere il rapporto sullo stato di avanzamento all'interno dello script. pexpect potrebbe essere usato per estrarre le informazioni:

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

Vedi Copia il file python nella rete locale (linux -> linux)

0
jfs