¿Cómo copiar un archivo a un servidor remoto en Python usando SCP o SSH?

Tengo un archivo de texto en mi máquina local generado por una secuencia de comandos de Python que se ejecuta en cron.

Me gustaría agregar un poco de código para que ese archivo se envíe de forma segura a mi servidor a través de SSH.

Si quieres el enfoque simple, esto debería funcionar.

Querrá “.close ()” el ​​archivo primero para que sepa que se ha descargado al disco desde Python.

import os os.system("scp FILE USER@SERVER:PATH") #eg os.system("scp foo.bar joe@srvr.net:/path/to/foo.bar") 

Debe generar (en la máquina de origen) e instalar (en la máquina de destino) una clave ssh de antemano para que el scp se autentique automáticamente con su clave pública ssh (en otras palabras, para que el script no solicite una contraseña) .

Ejemplo de ssh-keygen

Para hacer esto en Python (es decir, no envolver scp a través de subprocess.Popen o similar) con la biblioteca Paramiko , harías algo como esto:

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

(Probablemente desee tratar con hosts desconocidos, errores, crear cualquier directorio necesario, etc.).

Probablemente utilizarías el módulo de subproceso . Algo como esto:

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

Donde el destination es probablemente de la forma user@remotehost:remotepath . Gracias a @Charles Duffy por señalar la debilidad en mi respuesta original, que usó un solo argumento de cadena para especificar el shell=True operación scp shell=True , que no manejaría espacios en blanco en las rutas.

La documentación del módulo tiene ejemplos de comprobación de errores que puede realizar junto con esta operación.

Asegúrese de que ha configurado las credenciales adecuadas para poder realizar un scp desatendido y sin contraseña entre las máquinas . Hay una pregunta de stackoverflow para esto ya .

Hay un par de maneras diferentes de abordar el problema:

  1. Ajustar progtwigs de línea de comandos
  2. use una biblioteca de Python que proporciona capacidades SSH (por ejemplo, Paramiko o Twisted Conch )

Cada enfoque tiene sus propias peculiaridades. Necesitará configurar las claves SSH para habilitar los inicios de sesión sin contraseña si está ajustando los comandos del sistema como “ssh”, “scp” o “rsync”. Puede incrustar una contraseña en un script usando Paramiko o alguna otra biblioteca, pero puede encontrar la falta de documentación frustrante, especialmente si no está familiarizado con los conceptos básicos de la conexión SSH (por ejemplo, intercambio de claves, agentes, etc.). Probablemente no hace falta decir que las claves SSH son casi siempre una mejor idea que las contraseñas para este tipo de cosas.

NOTA: es difícil batir rsync si planea transferir archivos a través de SSH, especialmente si la alternativa es scp antiguo.

He utilizado Paramiko con el objective de reemplazar las llamadas al sistema, pero me encontré atado a los comandos envueltos debido a su facilidad de uso y familiaridad inmediata. Podrías ser diferente. Le di a Conch una vez más, pero no me atrajo.

Si opta por la ruta de llamada del sistema, Python ofrece una variedad de opciones como os.system o los módulos de comandos / subproceso. Iría con el módulo de subproceso si utilizara la versión 2.4+.

Llegó al mismo problema, pero en lugar de “piratear” o emular la línea de comandos:

Encontré esta respuesta aquí .

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

fabric podría ser utilizada para subir archivos 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() 

Puede usar el paquete vassal, que está diseñado exactamente para esto.

Todo lo que necesitas es instalar vassal y hacer

 from vassal.terminal import Terminal shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"]) shell.run() 

Además, le ahorrará la credencial de autenticación y no tendrá que escribirlas una y otra vez.

Llamar al comando scp través de un subproceso no permite recibir el informe de progreso dentro del script. pexpect podría ser utilizado para extraer esa información:

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

Ver el archivo de copia de python en la red local (linux -> linux)

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

Usé sshfs para montar el directorio remoto a través de ssh, y shutil para copiar los archivos:

 $ mkdir ~/sshmount $ sshfs user@remotehost:/path/to/remote/dst ~/sshmount 

Luego en python:

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

Este método tiene la ventaja de que puede transmitir datos si está generando datos en lugar de almacenar en caché localmente y enviar un solo archivo grande.

Intenta esto si no quieres usar certificados 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!') 

Un enfoque muy simple es el siguiente:

 import os os.system('sshpass -p "password" scp user@host:/path/to/file ./') 

No se requiere una biblioteca de Python (solo OS) y funciona

Tipo de hacky, pero lo siguiente debería funcionar 🙂

 import os filePath = "/foo/bar/baz.py" serverPath = "/blah/boo/boom.py" os.system("scp "+filePath+" user@myserver.com:"+serverPath)