Tengo un archivo de texto en mi máquina local que es generado por un script de Python que se ejecuta diariamente 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.
Tengo un archivo de texto en mi máquina local que es generado por un script de Python que se ejecuta diariamente 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.
Respuestas:
Puede llamar al scp
comando bash (copia archivos a través de SSH ) con subprocess.run
:
import subprocess
subprocess.run(["scp", FILE, "USER@SERVER:PATH"])
#e.g. subprocess.run(["scp", "foo.bar", "joe@srvr.net:/path/to/foo.bar"])
Si está creando el archivo que desea enviar en el mismo programa de Python, querrá llamar al subprocess.run
comando fuera del with
bloque que está usando para abrir el archivo (o llamar .close()
al archivo primero si no está usando un with
block), para que sepa que se descarga en el disco desde Python.
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) .
subprocess.check_call(['scp', srcfile, dest])
podría usarse desde Python 2.5 en lugar derc = Popen(..).wait(); if rc != 0: raise ..
Para hacer esto en Python (es decir, sin ajustar scp a través de subprocess.Popen o similar) con la biblioteca Paramiko , debería hacer 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 quiera lidiar con hosts desconocidos, errores, crear los directorios necesarios, etc.).
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
después de crear una instancia ssh
.
Probablemente usaría el módulo de subproceso . Algo como esto:
import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)
Donde destination
es probablemente de la formauser@remotehost:remotepath
. Gracias a @Charles Duffy por señalar la debilidad en mi respuesta original, que usaba un argumento de cadena única para especificar la operación scp shell=True
, que no manejaría los 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 haber configurado las credenciales adecuadas para poder realizar un scp desatendido y sin contraseña entre las máquinas . Ya hay una pregunta de stackoverflow para esto .
subprocess.check_call(['scp', myfile, destination])
podría usarse en su lugar desde Python 2.5 (2006)
Hay un par de formas diferentes de abordar el problema:
Cada enfoque tiene sus propias peculiaridades. Deberá configurar las claves SSH para habilitar inicios de sesión sin contraseña si está ajustando comandos del sistema como "ssh", "scp" o "rsync". Puede incrustar una contraseña en un script usando Paramiko o alguna otra biblioteca, pero la falta de documentación puede resultar frustrante, 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 superar a rsync si planea transferir archivos a través de SSH, especialmente si la alternativa es scp.
He usado Paramiko con miras a reemplazar las llamadas al sistema, pero me sentí atraído por los comandos envueltos debido a su facilidad de uso y familiaridad inmediata. Puede que seas diferente. Le di a Conch un vistazo hace algún tiempo pero no me atrajo.
Si opta por la ruta de llamada al sistema, Python ofrece una serie de opciones como os.system o los módulos de comandos / subprocesos. Iría con el módulo de subproceso si uso la versión 2.4+.
Llegó 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')
Puede hacer algo como esto, para manejar la verificación de la clave del host también
import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")
fabric
podría usarse 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()
Puede usar el paquete vasallo, que está diseñado exactamente para esto.
Todo lo que necesitas es instalar vasallo y hacer
from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()
Además, le ahorrará autenticar la credencial y no es necesario que la escriba una y otra vez.
Solía 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 almacenarlos en caché localmente y enviar un solo 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!')
Utilizando el recurso externo paramiko;
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')
Llamar al scp
comando a través de un 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 archivo de copia de python en la red local (linux -> linux)
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 sistema operativo), y funciona, sin embargo, el uso de este método depende de la instalación de otro cliente ssh. Esto podría resultar en un comportamiento no deseado si se ejecuta en otro sistema.
Un poco 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)