Comment scp en Python?

158

Quelle est la manière la plus pythonique de scp un fichier en Python? La seule voie que je connaisse est

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

qui est un hack, et qui ne fonctionne pas en dehors des systèmes de type Linux, et qui a besoin de l'aide du module Pexpect pour éviter les invites de mot de passe à moins que vous n'ayez déjà configuré SSH sans mot de passe sur l'hôte distant.

Je connais Twisted conch, mais je préférerais éviter d'implémenter scp moi-même via des modules ssh de bas niveau.

Je suis au courant d' paramikoun module Python qui prend en charge SSH et SFTP; mais il ne prend pas en charge SCP.

Contexte: je me connecte à un routeur qui ne prend pas en charge SFTP mais qui prend en charge SSH / SCP, donc SFTP n'est pas une option.

EDIT : Ceci est un double de Comment copier un fichier sur un serveur distant en Python en utilisant SCP ou SSH? . Cependant , cette question ne donne pas de réponse spécifique à scp qui traite des clés de Python. J'espère un moyen d'exécuter du code un peu comme

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')
Michael Gundlach
la source

Réponses:

106

Essayez le module Python scp pour Paramiko . C'est très simple à utiliser. Consultez l'exemple suivant:

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

Appelez ensuite scp.get()ou scp.put()pour effectuer des opérations SCP.

( Code SCPClient )

Tom Shen
la source
3
Sauf que le module n'utilise pas réellement paramiko - c'est beaucoup de code qui à la fin scpfinit par appeler la scpligne de commande qui ne fonctionne que sur * nix.
Nick Bastin
8
D'accord, ce que vous voyez dans le code source est l'appel à distance vers scp -tou scp -f(modes 'to' et 'from', qui existent pour ce but côté serveur). C'est essentiellement ainsi que fonctionne scp - il repose sur une autre copie de scp existant sur le serveur. Cet article l'explique très bien: blogs.oracle.com/janp/entry/how_the_scp_protocol_works
laher
8
Cette solution a fonctionné pour moi avec deux mises en garde: 1. voici les instructions d'importation que j'ai utilisées: import paramiko from scp import SCPClient2. Voici un exemple de la commande scp.get ():scp.get(r'/nfs_home/appers/xxxx/test2.txt', r'C:\Users\xxxx\Desktop\MR_Test')
Chris Nielsen
Cela fonctionne non seulement très bien, mais tire également parti des clés SSH si elles existent.
Marcel Wilson
Notez que vous pouvez également installer cette bibliothèque depuis PyPI pip install scp:, à la version 0.10.2 au moment de la rédaction de cet article.
Andrew B.
15

Vous pourriez être intéressé à essayer Pexpect ( code source ). Cela vous permettrait de traiter les invites interactives de votre mot de passe.

Voici un extrait d'un exemple d'utilisation (pour ftp) du site Web principal:

# 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 ('[email protected]')
child.expect ('ftp> ')
child.sendline ('cd pub')
child.expect('ftp> ')
child.sendline ('get ls-lR.gz')
child.expect('ftp> ')
child.sendline ('bye')
Pat Notz
la source
11

Vous pouvez également consulter paramiko . Il n'y a pas (encore) de module scp, mais il prend entièrement en charge sftp.

[EDIT] Désolé, j'ai manqué la ligne où vous avez mentionné paramiko. Le module suivant est simplement une implémentation du protocole scp pour paramiko. Si vous ne voulez pas utiliser paramiko ou conch (les seules implémentations ssh que je connaisse pour python), vous pouvez retravailler cela pour exécuter une session ssh régulière en utilisant des tubes.

scp.py pour paramiko

JimB
la source
Êtes-vous en train de dire que la solution jointe transférera en toute sécurité les fichiers vers n'importe quelle machine exécutant sshd, même si elle n'exécute pas sftpd? C'est exactement ce que je recherche, mais je ne peux pas dire à partir de votre commentaire si cela enveloppe juste sftp dans une façade de type scp.
Michael Gundlach
2
Cela transférera les fichiers avec n'importe quelle machine exécutant sshd, qui a scp dans le PATH (scp ne fait pas partie de la spécification ssh, mais c'est assez omniprésent). Cela appelle "scp -t" sur le serveur, et utilise le protocole scp pour transférer des fichiers, ce qui n'a rien à voir avec sftp.
JimB
1
Veuillez noter que j'ai utilisé l'implémentation de scp par openssh comme modèle, donc cela n'est pas garanti pour fonctionner avec d'autres versions. Certaines versions de sshd peuvent également utiliser le protocole scp2, qui est fondamentalement le même que sftp.
JimB
Pourriez-vous s'il vous plaît voir cette question: stackoverflow.com/questions/20111242/ ... Je me demande si paramiko utilise un sous-processus ou un autre élément comme les sockets. J'ai des problèmes de mémoire en utilisant subprocess.check_output ('ssh [email protected] "cat / data / file *") en raison de problèmes de clone / fork et je me demande si paramiko aura les mêmes problèmes ou non?
Paul
1
@Paul: paramiko n'aura pas les mêmes problèmes, car il n'utilise pas de sous-processus.
JimB
9

Impossible de trouver une réponse claire et ce module "scp.Client" n'existe pas. Au lieu de cela, cela me convient:

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')
Maviles
la source
6

si vous installez putty sur win32, vous obtenez un pscp (putty scp).

vous pouvez donc utiliser le hack os.system sur win32 aussi.

(et vous pouvez utiliser l'agent putty pour la gestion des clés)


désolé ce n'est qu'un hack (mais vous pouvez l'envelopper dans une classe python)

Blauohr
la source
La seule raison pour laquelle le 'nix one fonctionne est que vous avez SCP sur le chemin; comme le souligne Blauohr, ce n'est pas trop difficile à résoudre. +1
ojrac
6

Vous pouvez utiliser le sous-processus de package et l'appel de commande pour utiliser la commande scp à partir du shell.

from subprocess import call

cmd = "scp user1@host1:files user2@host2:files"
call(cmd.split(" "))
user7529863
la source
2
En quoi est-ce substantiellement différent du cas de base mentionné par l'intervenant? Oui, le sous-processus est meilleur que os.system, mais dans les deux cas, il ne fonctionne pas en dehors des systèmes de type Linux, a des problèmes avec les invites de mot de passe, etc.
Foon
5

À partir d'aujourd'hui, la meilleure solution est probablement 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)
Loïc
la source
1
Salut. Vous prétendez qu'AsyncSSH est le meilleur, mais pourriez-vous faire une déclaration ou deux pour soutenir cette affirmation? Peut-être le différencier de scp (), ou en quoi cela importait pour votre cas d'utilisation. (Cela dit, c'est beaucoup moins de code - du moins dans votre exemple)
Scott Prive
2
@Crossfit_and_Beer bonjour. Alors j'ai dit "probablement le meilleur", je laisserai le jugement à n'importe qui :) Je n'ai pas beaucoup de temps pour le moment pour comparer AsyncSSH à d'autres bibliothèques. Mais très rapidement: c'est simple à utiliser, c'est asynchrone, c'est maintenu et le maintener est très gentil et serviable, c'est assez complet et très bien documenté.
Loïc
5

Jetez un œil à 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/')
user443854
la source
2
Pourriez-vous être plus précis?
Nick T
4

Cela fait longtemps que cette question a été posée, et entre-temps, une autre bibliothèque capable de gérer cela a surgi: vous pouvez utiliser la fonction de copie incluse dans la bibliothèque 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)
pmos
la source
Comment puis-je saisir la phrase de passe de la clé privée avec Plumbum? p
ekta
@ekta: Vous serez invité à le saisir sur la ligne de commande. Si vous souhaitez le fournir à partir de votre propre code, je ne pense pas que l'API de plumbum le supporte. Mais plumbum's y SshMachinea accès ssh-agent, donc si vous voulez simplement éviter de le taper à chaque fois, c'est une façon de le faire. Vous pouvez également déposer une demande de fonctionnalité sur la page github de 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()
shrikkanth roxor
la source
Il serait utile que vous ajoutiez des commentaires à votre réponse expliquant pourquoi votre solution est bonne et aidiez les lecteurs à la comparer avec les autres solutions disponibles. Le simple fait de publier du code n'aide pas toujours l'apprenant à savoir comment reconnaître de meilleures solutions.
Brian Tompsett - 汤 莱恩
C'est SFTP, pas SCP.
Martin Prikryl le
2

Si vous êtes sur * nix, vous pouvez utiliser sshpass

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

Hmmm, une autre option serait peut-être d'utiliser quelque chose comme sshfs (il existe également un sshfs pour Mac). Une fois votre routeur monté, vous pouvez simplement copier les fichiers. Je ne sais pas si cela fonctionne pour votre application particulière, mais c'est une bonne solution à garder à portée de main.

Pat Notz
la source
1

Il y a quelque temps, j'ai mis en place un script de copie SCP python qui dépend de paramiko. Il comprend du code pour gérer les connexions avec une clé privée ou un agent de clé SSH avec un retour à l'authentification par mot de passe.

http://code.activestate.com/recipes/576810-copy-files-over-ssh-using-paramiko/

ccpizza
la source
Merci pour le lien et pour avoir pris le temps de publier un bon exemple d'utilisation de paramiko.
Peter M. - représente Monica