2008-09-16 14 views
74

J'ai un fichier texte sur ma machine locale qui est généré par un script Python quotidien exécuté dans cron.Comment copier un fichier sur un serveur distant en Python en utilisant SCP ou SSH?

Je voudrais ajouter un peu de code pour que ce fichier soit envoyé en toute sécurité à mon serveur via SSH.

+1

trouvé quelque chose de similaire, peut u un coup d'oeil http://stackoverflow.com/questions/11009308/copying-logs-in-python-using-the-command-line-function – JJ84

Répondre

36

Si vous voulez une approche simple, cela devrait fonctionner.

Vous devez d'abord ".close()" le fichier pour que vous sachiez qu'il est vidé sur le disque à partir de Python.

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

Vous devez générer (sur la machine source) et installer (sur la machine de destination) une clé ssh au préalable afin que le scp est automatiquement authentifié avec votre clé ssh publique (en d'autres termes, si votre script doesn ne demande pas de mot de passe).

ssh-keygen example

+26

subprocess.Popen (["scp", nom de fichier, "% (utilisateur) s @% (serveur) s:% (route distante) s"% vars]). wait() est une bien meilleure approche que os.system(), ce qui n'est pas le cas gérer les noms de fichiers avec des espaces correctement. –

+0

@Charles Quelle est la notation '%' que vous utilisez ici? Je voudrais lire la documentation sur ses fonctionnalités. – KomodoDave

+1

Peu importe, je l'ai trouvé. Recherchez "codes de format python" ou "opérations de mise en forme de chaîne python". L'opérateur '%' est appelé "opérateur de formatage de chaîne" ou "opérateur d'interpolation de chaîne". – KomodoDave

-1

Type de hacky, mais les éléments suivants devraient fonctionner :)

import os 
filePath = "/foo/bar/baz.py" 
serverPath = "/blah/boo/boom.py" 
os.system("scp "+filePath+" [email protected]:"+serverPath) 
28

Vous auriez probablement utiliser le subprocess module. Quelque chose comme ceci:

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

destination est probablement la forme [email protected]:remotepath. Merci à @ Charles Duffy pour avoir souligné la faiblesse de ma réponse initiale, qui utilisait un seul argument de chaîne pour spécifier l'opération scp shell=True - qui ne gérait pas les espaces dans les chemins.

La documentation du module a examples of error checking that you may want to perform in conjunction with this operation.

Assurez-vous que vous avez configuré les informations d'identification appropriées pour que vous puissiez effectuer une unattended, passwordless scp between the machines. Il y a un stackoverflow question for this already.

+3

L'utilisation de sous-processus.Popen est la bonne chose.Passer une chaîne plutôt qu'un tableau (et utiliser shell = True) est la fausse chose, car cela signifie que les noms de fichiers avec des espaces ne fonctionnent pas correctement. –

+1

au lieu de "sts = os.waitpid (p.pid, 0)", nous pouvons utiliser "sts = p.wait()". – db42

+0

existe-t-il un moyen d'exécution libre avec l'API python directe pour ce protocole? – lpapp

10

Il y a deux façons différentes d'aborder le problème:

  1. programmes de ligne de commande Wrap
  2. utiliser une bibliothèque Python qui fournit des fonctionnalités de SSH (par exemple - Paramiko ou Twisted Conch)

Chaque approche a ses propres caprices. Vous devrez configurer les clés SSH pour activer les connexions sans mot de passe si vous encapsulez des commandes système telles que "ssh", "scp" ou "rsync". Vous pouvez intégrer un mot de passe dans un script en utilisant Paramiko ou une autre bibliothèque, mais vous pourriez trouver le manque de documentation frustrant, surtout si vous n'êtes pas familier avec les bases de la connexion SSH (ex: échanges de clés, agents, etc.). Il va sans dire que les clés SSH sont presque toujours une meilleure idée que les mots de passe pour ce genre de choses. REMARQUE: il est difficile de battre rsync si vous envisagez de transférer des fichiers via SSH, surtout si l'alternative est plaine old scp. J'ai utilisé Paramiko dans le but de remplacer les appels système, mais je me suis retrouvé attiré par les commandes emballées en raison de leur facilité d'utilisation et de leur familiarité immédiate. Vous pourriez être différent. J'ai donné Conch une fois il y a quelque temps mais ça ne m'a pas plu.

Si vous optez pour le chemin d'appel système, Python offre un tableau d'options telles que os.system ou les modules de commandes/sous-processus. J'irais avec le module de sous-processus si vous utilisez la version 2.4+.

+1

curieux: quelle est l'histoire sur rsync vs scp? – Alok

113

Pour ce faire en Python (non enveloppant scp par subprocess.Popen ou similaire) avec la bibliothèque Paramiko, vous feriez quelque chose comme ceci:

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

(Vous auriez probablement eu envie de traiter avec des hôtes inconnus , erreurs, création de répertoires nécessaires, etc.).

+11

paramiko a aussi une belle fonction sftp.put (self, chemin local, chemin distant, callback = None), donc vous n'avez pas besoin d'ouvrir write et de fermer chaque fichier. –

+4

Je voudrais noter que SFTP n'est pas la même chose que SCP. – Drahkar

+4

@Drahkar la question demande que le fichier soit envoyé via SSH. C'est ce que ça fait. –

2

fabric pourrait être utilisé pour télécharger des fichiers 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() 
0

Appel commande scp via le sous-processus ne permet pas de recevoir le rapport d'avancement dans le script. pexpect pourrait être utilisé pour extraire cette information:

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

Voir python copy file in local network (linux -> linux)

4

atteint le même problème, mais au lieu de « piratage » ou émule la ligne de commande:

trouvé cette réponse here.

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

Remarque - cela dépend du paquetage scp. En décembre 2017, la dernière mise à jour de ce package date de 2015 et le numéro de version est 0.1.x. Je ne suis pas sûr de vouloir ajouter cette dépendance. – Chuck

0
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

une approche très simple est la suivante:

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

pas de bibliothèque Python sont nécessaires (seulement os) et il fonctionne

1

je sshfs pour monter le répertoire distant via ssh, et shutil pour copier les fichiers:

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

Puis, en python:

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

Cette méthode a l'avantage que vous pouvez diffuser des données sur si vous générer des données plutôt que la mise en cache localement et l'envoi d'un seul fichier.

Questions connexes