Pregunta

Tengo un archivo de texto en mi máquina local que se genera mediante un script Python diario 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.

¿Fue útil?

Solución

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

Primero querrás ".cerrar()" el ​​archivo para saber que está descargado al disco desde Python.

import os
os.system("scp FILE USER@SERVER:PATH")
#e.g. 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 ssh pública (en otras palabras, para que su script no solicite una contraseña) .

ejemplo de ssh-keygen

Otros consejos

Para hacer esto en Python (es decir,no envolver scp a través de subprocess.Popen o similar) con el Paramiko biblioteca, 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 querrás lidiar con hosts desconocidos, errores, crear los directorios necesarios, etc.).

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

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

Dónde destination probablemente sea de la forma user@remotehost:remotepath.Gracias a @Charles Duffy por señalar la debilidad en mi respuesta original, que utilizó un argumento de una sola cadena para especificar la operación SCP shell=True - Eso no manejaría espacios en blanco en las rutas.

La documentación del módulo tiene ejemplos de comprobación de errores que quizás desee realizar junto con esta operación.

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

Hay un par de formas diferentes de abordar el problema:

  1. Ajustar programas de línea de comandos
  2. utilice una biblioteca de Python que proporcione capacidades SSH (por ejemplo, Paramiko o Caracola retorcida)

Cada enfoque tiene sus propias peculiaridades.Deberá configurar las teclas SSH para habilitar inicios de sesión sin contraseña si está envolviendo 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, intercambios clave, 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 superar a rsync si planeas transferir archivos a través de SSH, especialmente si la alternativa es el viejo scp.

He usado Paramiko con miras a reemplazar las llamadas al sistema, pero me sentí atraído por los comandos empaquetados debido a su facilidad de uso y familiaridad inmediata.Puede que seas diferente.Le di una mirada a Conch hace algún tiempo pero no me atrajo.

Si opta por la ruta de llamada al sistema, Python ofrece una variedad de opciones como os.system o los módulos de comandos/subprocesos.Yo elegiría el módulo de subproceso si uso la versión 2.4+.

Llegué al mismo problema, pero en lugar de "piratear" o emular la línea de comando:

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 usarse para cargar archivos mediante 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()

Puedes utilizar el paquete vasallo, 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 guardará las credenciales de autenticación y no necesitará escribirlas una y otra vez.

Vocación scp El comando a través del subproceso no permite recibir el informe de progreso dentro del script. pexpect podría usarse 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 copiar archivo 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')

solía sshfs para montar el directorio remoto a través de ssh, y callar 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 almacenarlos en caché localmente y enviar un único archivo grande.

Pruebe esto si no desea utilizar 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 biblioteca de Python (solo sistema operativo) y funciona

Algo complicado, 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)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top