¿Cómo scp en Python?


158

¿Cuál es la forma más pitónica de scp un archivo en Python? La única ruta que conozco es

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

que es un truco, y que no funciona fuera de los sistemas similares a Linux, y que necesita ayuda del módulo Pexpect para evitar solicitudes de contraseña a menos que ya tenga una SSH sin contraseña configurada en el host remoto.

Soy consciente de Twisted conch, pero preferiría evitar implementar SCP a través de módulos ssh de bajo nivel.

Soy consciente de paramikoun módulo de Python que admite SSH y SFTP; pero no es compatible con SCP.

Antecedentes: me estoy conectando a un enrutador que no admite SFTP pero sí admite SSH / SCP, por lo que SFTP no es una opción.

EDITAR : ¿Este es un duplicado de Cómo copiar un archivo a un servidor remoto en Python usando SCP o SSH? . Sin embargo , esa pregunta no da una respuesta específica de scp que se ocupe de claves desde Python. Espero una forma de ejecutar código como

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

Respuestas:


106

Pruebe el módulo Python scp para Paramiko . Es muy fácil de usar. Vea el siguiente ejemplo:

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

Luego llame scp.get()o scp.put()para hacer operaciones SCP.

( Código de SCPClient )


3
Excepto que el módulo en realidad no usa paramiko, es una gran cantidad de código que al final scptermina llamando a la scplínea de comando que solo funciona en * nix.
Nick Bastin

8
De acuerdo, lo que se está viendo en el código fuente es la llamada remota a scp -to scp -f( 'a' y 'de' modos, que existen para este fin en el servidor). Así es esencialmente cómo funciona scp: se basa en otra copia de scp existente en el servidor. Este artículo lo explica muy bien: blogs.oracle.com/janp/entry/how_the_scp_protocol_works
laher

8
Esta solución me funcionó con dos advertencias: 1. Estas son las declaraciones de importación que utilicé: import paramiko from scp import SCPClient2. Aquí hay un ejemplo del comando scp.get ():scp.get(r'/nfs_home/appers/xxxx/test2.txt', r'C:\Users\xxxx\Desktop\MR_Test')
Chris Nielsen

Esto no solo funciona muy bien, sino que también aprovecha las claves SSH si existen.
Marcel Wilson

Tenga en cuenta que también puede instalar esta lib desde PyPI: pip install scpen la versión 0.10.2 a partir de este escrito.
Andrew B.

15

Quizás le interese probar Pexpect ( código fuente ). Esto le permitiría lidiar con solicitudes interactivas para su contraseña.

Aquí hay un fragmento de ejemplo de uso (para ftp) del sitio 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 ('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

También puedes ver paramiko . No hay un módulo scp (todavía), pero es totalmente compatible con sftp.

[EDITAR] Lo siento, perdí la línea donde mencionaste a paramiko. El siguiente módulo es simplemente una implementación del protocolo scp para paramiko. Si no desea utilizar paramiko o conch (las únicas implementaciones de ssh que conozco para python), puede volver a trabajar esto para ejecutarlo en una sesión de ssh normal utilizando tuberías.

scp.py para paramiko


¿Está diciendo que la solución adjunta transferirá de forma segura los archivos a cualquier máquina que ejecute sshd, incluso si no está ejecutando sftpd? Eso es exactamente lo que estoy buscando, pero no puedo decir por su comentario si esto simplemente envuelve sftp en una fachada similar a scp.
Michael Gundlach

2
Esto transferirá archivos con cualquier máquina que ejecute sshd, que tiene scp en la RUTA (scp no es parte de la especificación ssh, pero es bastante omnipresente). Esto invoca "scp -t" en el servidor y utiliza el protocolo scp para transferir archivos, que no tiene nada que ver con sftp.
JimB

1
Tenga en cuenta que usé la implementación de scsh de openssh como modelo, por lo que no se garantiza que funcione con otras versiones. Algunas versiones de sshd también pueden usar el protocolo scp2, que es básicamente lo mismo que sftp.
JimB

¿Podría ver esta pregunta? Stackoverflow.com/questions/20111242/… Me pregunto si paramiko usa un subproceso o algo más como sockets. Tengo problemas de memoria con subprocess.check_output ('ssh blah@blah.com "cat / data / file *") debido a problemas de clonación / bifurcación y me pregunto si paramiko tendrá los mismos problemas o no.
Paul

1
@Paul: paramiko no tendrá los mismos problemas, ya que no utiliza un subproceso.
JimB

9

No se pudo encontrar una respuesta directa, y este módulo "scp.Client" no existe. En cambio, esto me conviene:

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

si instala putty en win32 obtendrá un pscp (putty scp).

así que también puedes usar el hack de os.system en win32.

(y puede usar el agente de masilla para la administración de claves)


lo siento, es solo un hack (pero puedes envolverlo en una clase de python)


La única razón por la que el 'nix one funciona es que tienes SCP en el camino; Como señala Blauohr, eso no es demasiado difícil de solucionar. +1
ojrac

6

Puede usar el subproceso del paquete y la llamada de comando para usar el comando scp desde el shell.

from subprocess import call

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

2
¿Cómo es esto sustancialmente diferente del caso base mencionado por el interlocutor? Sí, el subproceso es mejor que os.system, pero en ambos casos, no funciona fuera de los sistemas tipo linux, tiene problemas con las solicitudes de contraseña, etc.
Foon

5

A partir de hoy, la mejor solución es probablemente 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
Hola. Usted hace un reclamo acerca de que AsyncSSH es el mejor, pero ¿podría hacer una declaración o 2 para respaldar este reclamo? Quizás lo diferencie de scp (), o cómo importó para su caso de uso. (Dicho esto, es mucho menos código, al menos en su ejemplo)
Scott Prive

2
@Crossfit_and_Beer hola. Entonces dije "probablemente el mejor", dejaré el juicio a cualquiera :) No tengo mucho tiempo en este momento para comparar AsyncSSH con otras bibliotecas. Pero muy rápido: es fácil de usar, es asíncrono, se mantiene y el mantenedor es muy agradable y útil, es bastante completo y está muy bien documentado.
Loïc

5

Echa un vistazo 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
¿Podrías ser más específico?
Nick T

4

Ha pasado bastante tiempo desde que se hizo esta pregunta, y mientras tanto, otra biblioteca que puede manejar esto ha surgido: puede usar la función de copia incluida en la biblioteca 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)

¿Cómo puedo ingresar la frase de contraseña para la clave privada con Plumbum? p
ekta

@ekta: se le solicitará en la línea de comando. Si desea proporcionarlo desde su propio código, no creo que la API de plumbum lo admita. Pero Plumbum SshMachinetiene acceso ssh-agent, así que si solo quieres evitar escribirlo todo el tiempo, esa es una forma de hacerlo. También puede presentar una solicitud de función en la página de 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()

Sería útil si agregara algún comentario a su respuesta que explicara por qué su solución es buena y ayude a los lectores a compararla con las otras soluciones disponibles. Solo publicar código no siempre ayuda a los alumnos a saber cómo reconocer mejores soluciones.
Brian Tompsett - 汤 莱恩

Esto es SFTP, no SCP.
Martin Prikryl

2

Si está en * nix, puede usar sshpass

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

1

Hmmm, quizás otra opción sería usar algo como sshfs (también hay un sshfs para Mac). Una vez que su enrutador está montado, puede copiar los archivos directamente. No estoy seguro de si eso funciona para su aplicación en particular, pero es una buena solución para tener a mano.



Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.