2008-09-16 23 views
74

Tengo un archivo de texto en mi equipo local que se genera mediante un script diario de Python ejecutado en cron.¿Cómo copiar un archivo a un servidor remoto en Python usando SCP o SSH?

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

+1

encontrado algo similar, u puede tener una mirada http://stackoverflow.com/questions/11009308/copying-logs-in-python-using-the-command-line-function – JJ84

Respuesta

36

Si desea un enfoque simple, esto debería funcionar.

Querrá ".close()" el ​​archivo primero para que sepa que se ha eliminado del disco de Python.

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

necesita generar (en la máquina de origen) e instalar (en la máquina de destino) una clave SSH de antemano para que el SCP automáticamente se autentica con su clave pública SSH (en otras palabras, por lo que su guión doesn no pida una contraseña).

ssh-keygen example

+26

subprocess.Popen (["scp", filename, "% (usuario) s @% (servidor) s:% (remotepath) s"% vars]). wait() es un enfoque mucho mejor que os.system(), que no manejar nombres de archivo con espacios correctamente. –

+0

@Charles ¿Cómo se llama la notación '%' que está utilizando aquí? Me gustaría leer la documentación sobre su funcionalidad. – KomodoDave

+1

No importa, lo encontré. Busque "códigos de formato python" o "operaciones de formato de cadena python". El operador '%' se conoce como el "operador de formateo de cadenas" o el "operador de interpolación de cadenas". – KomodoDave

-1

tipo de hacky, pero los siguientes deben trabajar :)

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

lo que probablemente utiliza el subprocess module. Algo como esto:

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

Dónde destination es, probablemente, de la forma [email protected]:remotepath. Gracias a @Charles Duffy por señalar la debilidad en mi respuesta original, que utilizaba un argumento de cadena única para especificar la operación scp shell=True - que no manejaría el espacio en blanco en las rutas.

La documentación del módulo tiene examples of error checking that you may want to perform in conjunction with this operation.

Asegúrese de que haya configurado las credenciales adecuadas para que pueda realizar una unattended, passwordless scp between the machines. Hay un stackoverflow question for this already.

+3

Usando subproceso.Popen es lo correcto.Pasarle una cadena en lugar de una matriz (y usar shell = True) es lo incorrecto, ya que significa que los nombres de archivo con espacios no funcionan correctamente. –

+1

en lugar de "sts = os.waitpid (p.pid, 0)", podemos usar "sts = p.wait()". – db42

+0

¿hay una manera libre de ejecución con API directa de Python para este protocolo? – lpapp

10

Hay un par de diferentes maneras de abordar el problema:

  1. programas de línea de comandos Wrap
  2. utilizar una biblioteca de Python que proporciona capacidades de SSH (por ejemplo - Paramiko o Twisted Conch)

Cada enfoque tiene sus propias peculiaridades. Tendrá que configurar las claves SSH para habilitar inicios de sesión sin contraseña si está envolviendo comandos del sistema como "ssh", "scp" o "rsync". Puede insertar una contraseña en un script utilizando Paramiko o alguna otra biblioteca, pero puede encontrar frustrante la falta de documentación, especialmente si no está familiarizado con los conceptos básicos de la conexión SSH (por ejemplo, intercambios 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 de superar rsync si planea transferir archivos a través de SSH, especialmente si la alternativa es simple viejo scp.

He utilizado Paramiko con miras a reemplazar las llamadas al sistema, pero me encontré de regreso a los comandos envueltos debido a su facilidad de uso y familiaridad inmediata. Usted puede ser diferente. Le di la vuelta a Conch hace algún tiempo pero no me atraía.

Si opta por la ruta de la llamada al sistema, Python ofrece una serie de opciones como os.system o los comandos/subprocesos de los módulos. Iré con el módulo de subproceso si uso la versión 2.4+.

+1

curioso: ¿cuál es la historia en rsync vs. scp? – Alok

113

Para hacer esto en Python (es decir, no a través de envolver SCP subprocess.Popen o similar) con la biblioteca Paramiko, que haría 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 querría hacer frente a los ejércitos desconocidos , errores, creación de directorios necesarios, etc.).

+11

paramiko también tiene una bonita función sftp.put (self, localpath, remotepath, callback = None), por lo que no tiene que abrir la escritura y cerrar cada archivo. –

+4

Me gustaría señalar que SFTP no es lo mismo que SCP. – Drahkar

+4

@Drahkar la pregunta pide que el archivo se envíe a través de SSH. Eso es lo que hace esto. –

2

fabric podrían utilizarse para cargar 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() 
0

Calling scp comando a través de subproceso no permite recibir el informe de situación dentro del guión. pexpect podrían utilizarse 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 python copy file in local network (linux -> linux)

4

alcanzado el mismo problema, pero en lugar de la "piratería" o emulando línea de comandos:

encontrado esta respuesta 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

Nota: esto depende del paquete scp. A partir de diciembre de 2017, la última actualización de ese paquete fue en 2015 y el número de versión es 0.1.x. No estoy seguro de querer agregar esta dependencia. – 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

un enfoque muy simple es la siguiente:

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

se requiere ninguna biblioteca de Python (sólo OS) y funciona

1

Solía ​​sshfs para montar el directorio remoto a través ssh, y shutil para copiar los archivos:

$ mkdir ~/sshmount 
$ sshfs [email protected]:/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 a través de si está generando datos en lugar de almacenamiento en caché local y el envío de un solo archivo grande.

Cuestiones relacionadas