Ho un file di testo sulla mia macchina locale che viene generato da uno script Python giornaliero eseguito in cron.
Vorrei aggiungere un po 'di codice per inviare il file in modo sicuro al mio server tramite SSH.
Ho un file di testo sulla mia macchina locale che viene generato da uno script Python giornaliero eseguito in cron.
Vorrei aggiungere un po 'di codice per inviare il file in modo sicuro al mio server tramite SSH.
Risposte:
Puoi chiamare il scp
comando bash (copia i file su SSH ) con subprocess.run
:
import subprocess
subprocess.run(["scp", FILE, "USER@SERVER:PATH"])
#e.g. subprocess.run(["scp", "foo.bar", "joe@srvr.net:/path/to/foo.bar"])
Se stai creando il file che desideri inviare nello stesso programma Python, ti consigliamo di chiamare il subprocess.run
comando al di fuori del with
blocco che stai utilizzando per aprire il file (o chiamare .close()
prima il file se non stai utilizzando un with
block), quindi sai che è scaricato su disco da Python.
Devi generare (sulla macchina di origine) e installare (sulla macchina di destinazione) una chiave ssh in anticipo in modo che scp venga automaticamente autenticato con la tua chiave ssh pubblica (in altre parole, in modo che il tuo script non richieda una password) .
subprocess.check_call(['scp', srcfile, dest])
potrebbe essere utilizzato da Python 2.5 invece dirc = Popen(..).wait(); if rc != 0: raise ..
Per fare questo in Python (cioè non avvolgere scp attraverso subprocess.Popen o simili) con la libreria Paramiko , dovresti fare qualcosa del genere:
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 vorresti occuparti di host sconosciuti, errori, creare le directory necessarie e così via).
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
dopo l'istanza ssh
.
Probabilmente useresti il modulo subprocess . Qualcosa come questo:
import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)
Dov'è destination
probabilmente della forma user@remotehost:remotepath
. Grazie a @Charles Duffy per aver sottolineato la debolezza nella mia risposta originale, che utilizzava un singolo argomento di stringa per specificare l'operazione scpshell=True
che non avrebbe gestito gli spazi bianchi nei percorsi.
La documentazione del modulo ha esempi di controllo degli errori che potresti voler eseguire insieme a questa operazione.
Assicurati di aver impostato le credenziali appropriate in modo da poter eseguire uno scp non assistito e senza password tra le macchine . C'è già una domanda di stackoverflow per questo .
subprocess.check_call(['scp', myfile, destination])
potrebbe essere usato invece da Python 2.5 (2006)
Ci sono un paio di modi diversi per affrontare il problema:
Ogni approccio ha le sue peculiarità. Dovrai configurare le chiavi SSH per abilitare gli accessi senza password se stai eseguendo il wrapping di 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 genere di cose.
NOTA: è difficile battere rsync se prevedi di trasferire file tramite SSH, specialmente se l'alternativa è semplicemente il vecchio scp.
Ho usato Paramiko con l'obiettivo di sostituire le chiamate di sistema, ma mi sono ritrovato attratto dai comandi impacchettati grazie alla loro facilità d'uso e familiarità immediata. Potresti essere diverso. Ho dato a Conch una riprova qualche tempo fa, ma non mi piaceva.
Se si opta per il percorso della chiamata di sistema, Python offre una serie di opzioni come os.system oi moduli di comandi / sottoprocesso. Vorrei andare con il modulo sottoprocesso se si utilizza la versione 2.4+.
Hai raggiunto lo stesso problema, ma invece di "hackerare" o emulare la riga di comando:
Ho 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')
Puoi fare qualcosa del genere, per gestire anche il controllo della chiave host
import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")
fabric
potrebbe essere utilizzato per caricare file vis ssh:
#!/usr/bin/env python
from fabric.api import execute, put
from fabric.network import disconnect_all
if __name__=="__main__":
import sys
# specify hostname to connect to and the remote/local paths
srcdir, remote_dirname, hostname = sys.argv[1:]
try:
s = execute(put, srcdir, remote_dirname, host=hostname)
print(repr(s))
finally:
disconnect_all()
Puoi usare il pacchetto vassallo, che è esattamente progettato per questo.
Tutto ciò di cui hai bisogno è installare vassallo e farlo
from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()
Inoltre, ti salverà le credenziali di autenticazione e non sarà necessario digitarle ancora e ancora.
Ho usato sshfs per montare la directory remota tramite ssh e shutil per copiare i file:
$ mkdir ~/sshmount
$ sshfs user@remotehost:/path/to/remote/dst ~/sshmount
Quindi in Python:
import shutil
shutil.copy('a.txt', '~/sshmount')
Questo metodo ha il vantaggio di poter trasmettere dati in streaming se si generano dati anziché memorizzarli nella cache locale e inviare un singolo file di grandi dimensioni.
Prova questo se non vuoi utilizzare 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!')
Utilizzando la risorsa esterna paramiko;
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')
La chiamata al scp
comando tramite sottoprocesso non consente di ricevere il rapporto sullo stato di avanzamento all'interno dello script. pexpect
potrebbe essere utilizzato per estrarre tali 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 il file di copia python nella rete locale (linux -> linux)
Un approccio molto semplice è il seguente:
import os
os.system('sshpass -p "password" scp user@host:/path/to/file ./')
Non è richiesta alcuna libreria python (solo os) e funziona, tuttavia l'utilizzo di questo metodo si basa su un altro client ssh da installare. Ciò potrebbe causare un comportamento indesiderato se eseguito su un altro sistema.
Un po 'hacky, ma quanto segue dovrebbe funzionare :)
import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)