Saya memiliki file teks di mesin lokal saya yang dihasilkan oleh skrip Python harian yang dijalankan di cron.
Saya ingin menambahkan sedikit kode agar file tersebut dikirim dengan aman ke server saya melalui SSH.
Saya memiliki file teks di mesin lokal saya yang dihasilkan oleh skrip Python harian yang dijalankan di cron.
Saya ingin menambahkan sedikit kode agar file tersebut dikirim dengan aman ke server saya melalui SSH.
Jawaban:
Anda dapat memanggil scp
perintah bash (ini menyalin file melalui SSH ) dengan 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"])
Jika Anda membuat file yang ingin Anda kirim dalam program Python yang sama, Anda akan ingin memanggil subprocess.run
perintah di luar with
blok yang Anda gunakan untuk membuka file (atau panggil .close()
file terlebih dahulu jika Anda tidak menggunakan with
block), jadi Anda tahu itu di-flush ke disk dari Python.
Anda perlu membuat (pada mesin sumber) dan menginstal (pada mesin tujuan) kunci ssh sebelumnya sehingga scp secara otomatis diautentikasi dengan kunci ssh publik Anda (dengan kata lain, sehingga skrip Anda tidak meminta kata sandi) .
subprocess.check_call(['scp', srcfile, dest])
dapat digunakan sejak Python 2.5 alih-alihrc = Popen(..).wait(); if rc != 0: raise ..
Untuk melakukan ini dengan Python (yaitu tidak membungkus scp melalui subprocess.Popen atau serupa) dengan pustaka Paramiko , Anda akan melakukan sesuatu seperti ini:
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()
(Anda mungkin ingin berurusan dengan host yang tidak dikenal, kesalahan, membuat direktori yang diperlukan, dan sebagainya).
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
setelah membuat contoh ssh
.
Anda mungkin akan menggunakan modul subproses . Sesuatu seperti ini:
import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)
Dimana destination
mungkin bentuknya user@remotehost:remotepath
. Terima kasih kepada @Charles Duffy karena menunjukkan kelemahan dalam jawaban asli saya, yang menggunakan argumen string tunggal untuk menentukan operasi scp shell=True
- yang tidak akan menangani spasi kosong di jalur.
Dokumentasi modul memiliki contoh pemeriksaan kesalahan yang mungkin ingin Anda lakukan sehubungan dengan operasi ini.
Pastikan Anda telah menyiapkan kredensial yang tepat sehingga Anda dapat melakukan scp tanpa pengawasan tanpa kata sandi di antara mesin . Sudah ada pertanyaan stackoverflow untuk ini .
subprocess.check_call(['scp', myfile, destination])
dapat digunakan sebagai gantinya sejak Python 2.5 (2006)
Ada beberapa cara berbeda untuk menangani masalah:
Setiap pendekatan memiliki keunikannya sendiri. Anda perlu mengatur kunci SSH untuk mengaktifkan login tanpa kata sandi jika Anda menggabungkan perintah sistem seperti "ssh", "scp" atau "rsync." Anda dapat menyematkan kata sandi dalam skrip menggunakan Paramiko atau pustaka lain, tetapi Anda mungkin merasa kekurangan dokumentasi membuat frustrasi, terutama jika Anda tidak terbiasa dengan dasar-dasar koneksi SSH (misalnya - pertukaran kunci, agen, dll). Mungkin tidak perlu dikatakan bahwa kunci SSH hampir selalu merupakan ide yang lebih baik daripada kata sandi untuk hal semacam ini.
CATATAN: sulit untuk mengalahkan rsync jika Anda berencana mentransfer file melalui SSH, terutama jika alternatifnya adalah scp lama biasa.
Saya telah menggunakan Paramiko dengan tujuan untuk mengganti panggilan sistem tetapi mendapati diri saya ditarik kembali ke perintah yang dibungkus karena kemudahan penggunaan dan keakraban langsungnya. Anda mungkin berbeda. Saya memberikan Conch sekali lagi beberapa waktu lalu tetapi itu tidak menarik bagi saya.
Jika memilih jalur panggilan sistem, Python menawarkan serangkaian opsi seperti os.system atau modul perintah / subproses. Saya akan menggunakan modul subproses jika menggunakan versi 2.4+.
Mencapai masalah yang sama, tetapi bukannya "meretas" atau meniru baris perintah:
Temukan jawaban ini di sini .
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')
Anda dapat melakukan sesuatu seperti ini, untuk menangani pemeriksaan kunci host juga
import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")
fabric
dapat digunakan untuk mengunggah file 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()
Anda dapat menggunakan paket pengikut, yang dirancang khusus untuk ini.
Yang Anda butuhkan hanyalah menginstal pengikut dan melakukannya
from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()
Selain itu, ini akan menghemat otentikasi kredensial dan tidak perlu mengetiknya lagi dan lagi.
Saya menggunakan sshfs untuk memasang direktori jarak jauh melalui ssh, dan shutil untuk menyalin file:
$ mkdir ~/sshmount
$ sshfs user@remotehost:/path/to/remote/dst ~/sshmount
Kemudian di python:
import shutil
shutil.copy('a.txt', '~/sshmount')
Metode ini memiliki keuntungan karena Anda dapat mengalirkan data jika Anda membuat data daripada melakukan caching secara lokal dan mengirim satu file besar.
Coba ini jika Anda tidak ingin menggunakan sertifikat 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!')
Menggunakan paramiko sumber daya eksternal;
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')
Memanggil scp
perintah melalui subproses tidak memungkinkan untuk menerima laporan kemajuan di dalam skrip. pexpect
dapat digunakan untuk mengekstrak info itu:
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})
Pendekatan yang sangat sederhana adalah sebagai berikut:
import os
os.system('sshpass -p "password" scp user@host:/path/to/file ./')
Tidak ada pustaka python yang diperlukan (hanya os), dan ini berfungsi, namun menggunakan metode ini bergantung pada klien ssh lain yang akan diinstal. Ini dapat menyebabkan perilaku yang tidak diinginkan jika dijalankan di sistem lain.
Agak hacky, tetapi yang berikut harus berfungsi :)
import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)