web-development-kb-es.site

¿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.

89
Alok

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 [email protected]:PATH")
#e.g. os.system("scp foo.bar [email protected]:/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

45
pdq

Para hacer esto en Python (es decir, no ajustar scp a través de subprocess.Popen o similar) con Paramiko library, 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.).

134
Tony Meyer

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

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

Donde destination es probablemente de la forma [email protected]:remotepath. Gracias a @Charles Duffy por señalar la debilidad en mi respuesta original, que usó un único argumento de cadena para especificar la 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 es posible que desee realizar junto con esta operación.

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

28
Blair Conrad

Hay un par de maneras diferentes de abordar el problema:

  1. Ajustar programas de línea de comandos
  2. usar 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 insertar 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 objetivo 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+.

11
Michael

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

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 [email protected]:/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.

1
Shawn
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
michael

un enfoque muy simple es el siguiente:

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

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

0

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!')
0
JavDomGom

Usé sshfs para montar el directorio remoto a través de 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 si está generando datos en lugar de almacenar en caché localmente y enviar un solo archivo grande.

0
Jonno_FTW

Llamar al comando scp 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})

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

0
jfs