Ich habe eine Textdatei auf meinem lokalen Computer, die von einem täglichen Python-Skript generiert wird, das in cron ausgeführt wird.
Ich möchte ein bisschen Code hinzufügen, damit diese Datei sicher über SSH an meinen Server gesendet wird.
Ich habe eine Textdatei auf meinem lokalen Computer, die von einem täglichen Python-Skript generiert wird, das in cron ausgeführt wird.
Ich möchte ein bisschen Code hinzufügen, damit diese Datei sicher über SSH an meinen Server gesendet wird.
Antworten:
Sie können den scp
Befehl bash (er kopiert Dateien über SSH ) aufrufen mit 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"])
Wenn Sie die Datei erstellen, die Sie im selben Python-Programm senden möchten, möchten Sie den subprocess.run
Befehl außerhalb des with
Blocks aufrufen, den Sie zum Öffnen der Datei verwenden (oder .close()
die Datei zuerst aufrufen, wenn Sie keine verwenden with
block), damit Sie wissen, dass es von Python auf die Festplatte geschrieben wurde.
Sie müssen zuvor (auf dem Quellcomputer) einen SSH-Schlüssel generieren und (auf dem Zielcomputer) installieren, damit der SCP automatisch mit Ihrem öffentlichen SSH-Schlüssel authentifiziert wird (mit anderen Worten, Ihr Skript fragt nicht nach einem Kennwort). .
subprocess.check_call(['scp', srcfile, dest])
könnte seit Python 2.5 anstelle vonrc = Popen(..).wait(); if rc != 0: raise ..
Um dies in Python (dh scp nicht durch subprocess.Popen oder ähnliches zu verpacken ) mit der Paramiko- Bibliothek zu tun, würden Sie Folgendes tun:
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()
(Sie möchten wahrscheinlich unbekannte Hosts, Fehler, das Erstellen von erforderlichen Verzeichnissen usw. behandeln.)
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
sofort funktioniert, fügen Sie nach dem Instanziieren hinzu ssh
.
Sie würden wahrscheinlich das Unterprozessmodul verwenden . Etwas wie das:
import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)
Wo destination
ist wohl von der Form user@remotehost:remotepath
. Vielen Dank an @Charles Duffy für den Hinweis auf die Schwachstelle in meiner ursprünglichen Antwort, die ein einziges Zeichenfolgenargument zur Angabe der scp-Operation verwendete shell=True
- das würde Leerzeichen in Pfaden nicht behandeln.
Die Moduldokumentation enthält Beispiele für die Fehlerprüfung, die Sie möglicherweise in Verbindung mit diesem Vorgang ausführen möchten.
Stellen Sie sicher, dass Sie die richtigen Anmeldeinformationen eingerichtet haben, damit Sie eine unbeaufsichtigte, kennwortlose Überprüfung zwischen den Computern durchführen können . Hierfür gibt es bereits eine Stackoverflow-Frage .
subprocess.check_call(['scp', myfile, destination])
könnte stattdessen seit Python 2.5 (2006)
Es gibt verschiedene Möglichkeiten, um das Problem anzugehen:
Jeder Ansatz hat seine eigenen Macken. Sie müssen SSH-Schlüssel einrichten, um kennwortlose Anmeldungen zu aktivieren, wenn Sie Systembefehle wie "ssh", "scp" oder "rsync" umschließen. Sie können ein Kennwort mithilfe von Paramiko oder einer anderen Bibliothek in ein Skript einbetten. Der Mangel an Dokumentation ist jedoch möglicherweise frustrierend, insbesondere wenn Sie mit den Grundlagen der SSH-Verbindung nicht vertraut sind (z. B. Schlüsselaustausch, Agenten usw.). Es versteht sich wahrscheinlich von selbst, dass SSH-Schlüssel für solche Dinge fast immer eine bessere Idee sind als Passwörter.
HINWEIS: Es ist schwer, rsync zu schlagen, wenn Sie Dateien über SSH übertragen möchten, insbesondere wenn die Alternative einfach altes scp ist.
Ich habe Paramiko verwendet, um Systemaufrufe zu ersetzen, fand mich jedoch aufgrund ihrer Benutzerfreundlichkeit und sofortigen Vertrautheit zu den umschlossenen Befehlen zurückgezogen. Du könntest anders sein. Ich habe Conch vor einiger Zeit noch einmal gegeben, aber es hat mich nicht angesprochen.
Wenn Sie sich für den Systemaufrufpfad entscheiden, bietet Python eine Reihe von Optionen wie os.system oder die Befehle / Unterprozessmodule. Ich würde mit dem Subprozessmodul gehen, wenn ich Version 2.4+ verwenden würde.
Das gleiche Problem erreicht, aber anstatt die Befehlszeile zu "hacken" oder zu emulieren:
Fand diese Antwort hier .
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')
Sie können so etwas tun, um auch die Überprüfung des Hostschlüssels durchzuführen
import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")
fabric
könnte verwendet werden, um Dateien vis ssh hochzuladen:
#!/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()
Sie können das Vasallenpaket verwenden, das genau dafür ausgelegt ist.
Alles was Sie brauchen, ist Vasall zu installieren und zu tun
from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()
Außerdem werden Sie authentifizierte Anmeldeinformationen speichern und müssen diese nicht immer wieder eingeben.
Ich habe sshfs verwendet , um das Remote-Verzeichnis über ssh bereitzustellen , und shutil, um die Dateien zu kopieren:
$ mkdir ~/sshmount
$ sshfs user@remotehost:/path/to/remote/dst ~/sshmount
Dann in Python:
import shutil
shutil.copy('a.txt', '~/sshmount')
Diese Methode hat den Vorteil, dass Sie Daten streamen können, wenn Sie Daten generieren, anstatt sie lokal zwischenzuspeichern und eine einzelne große Datei zu senden.
Versuchen Sie dies, wenn Sie keine SSL-Zertifikate verwenden möchten:
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!')
Verwenden der externen Ressource 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')
Durch Aufrufen des scp
Befehls über einen Unterprozess kann der Fortschrittsbericht nicht im Skript empfangen werden. pexpect
könnte verwendet werden, um diese Informationen zu extrahieren:
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})
Siehe Python-Kopierdatei im lokalen Netzwerk (Linux -> Linux)
Ein sehr einfacher Ansatz ist der folgende:
import os
os.system('sshpass -p "password" scp user@host:/path/to/file ./')
Es ist keine Python-Bibliothek erforderlich (nur Betriebssystem), und es funktioniert. Die Verwendung dieser Methode setzt jedoch voraus, dass ein anderer SSH-Client installiert wird. Dies kann zu unerwünschtem Verhalten führen, wenn es auf einem anderen System ausgeführt wird.
Ein bisschen hacky, aber das Folgende sollte funktionieren :)
import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)