ฉันมีไฟล์ข้อความในเครื่องของฉันซึ่งสร้างโดยสคริปต์ Python ทุกวันที่ทำงานใน cron
ฉันต้องการเพิ่มโค้ดเล็กน้อยเพื่อให้ไฟล์นั้นถูกส่งไปยังเซิร์ฟเวอร์ของฉันอย่างปลอดภัยผ่าน SSH
ฉันมีไฟล์ข้อความในเครื่องของฉันซึ่งสร้างโดยสคริปต์ Python ทุกวันที่ทำงานใน cron
ฉันต้องการเพิ่มโค้ดเล็กน้อยเพื่อให้ไฟล์นั้นถูกส่งไปยังเซิร์ฟเวอร์ของฉันอย่างปลอดภัยผ่าน SSH
คำตอบ:
คุณสามารถเรียกscp
คำสั่ง bash (มันคัดลอกไฟล์ผ่านSSH ) ด้วย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"])
หากคุณกำลังสร้างไฟล์ที่คุณต้องการส่งในโปรแกรม Python เดียวกันคุณจะต้องเรียกsubprocess.run
คำสั่งนอกwith
บล็อกที่คุณใช้เพื่อเปิดไฟล์ (หรือเรียก.close()
ไฟล์ก่อนหากคุณไม่ได้ใช้with
block) ดังนั้นคุณจะรู้ว่ามันถูกล้างไปยังดิสก์จาก Python
คุณต้องสร้าง (บนเครื่องต้นทาง) และติดตั้ง (บนเครื่องปลายทาง) คีย์ ssh ไว้ล่วงหน้าเพื่อให้ scp ได้รับการรับรองความถูกต้องโดยอัตโนมัติด้วยคีย์ ssh สาธารณะของคุณ (กล่าวอีกนัยหนึ่งสคริปต์ของคุณจะไม่ขอรหัสผ่าน) .
subprocess.check_call(['scp', srcfile, dest])
สามารถใช้ได้ตั้งแต่ Python 2.5 แทนที่จะเป็นrc = Popen(..).wait(); if rc != 0: raise ..
หากต้องการทำสิ่งนี้ใน Python (เช่นไม่ตัด scp ผ่านกระบวนการย่อยเปิดหรือคล้ายกัน) กับไลบรารีParamikoคุณต้องทำสิ่งนี้:
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()
(คุณอาจต้องการจัดการกับโฮสต์ที่ไม่รู้จักข้อผิดพลาดการสร้างไดเรกทอรีใด ๆ ที่จำเป็นและอื่น ๆ )
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh
คุณอาจต้องการใช้โมดูลกระบวนการย่อย สิ่งนี้:
import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)
ในกรณีที่อาจจะเป็นในรูปแบบdestination
user@remotehost:remotepath
ขอบคุณ @Charles Duffy ที่ชี้ให้เห็นจุดอ่อนในคำตอบเดิมของฉันซึ่งใช้อาร์กิวเมนต์สตริงเดียวเพื่อระบุการดำเนินการ scp shell=True
ซึ่งจะไม่จัดการช่องว่างในเส้นทาง
เอกสารประกอบโมดูลมีตัวอย่างการตรวจสอบข้อผิดพลาดที่คุณอาจต้องการดำเนินการร่วมกับการดำเนินการนี้
ตรวจสอบให้แน่ใจว่าคุณได้ตั้งค่าข้อมูลประจำตัวที่เหมาะสมเพื่อให้คุณสามารถดำเนินการแบบอัตโนมัติ SCP passwordless ระหว่างเครื่อง มีเป็นคำถาม StackOverflow สำหรับเรื่องนี้อยู่แล้ว
subprocess.check_call(['scp', myfile, destination])
สามารถใช้แทนได้ตั้งแต่ Python 2.5 (2006)
มีสองวิธีในการแก้ไขปัญหา:
แต่ละแนวทางมีนิสัยใจคอของตัวเอง คุณจะต้องตั้งค่าคีย์ SSH เพื่อเปิดใช้งานการเข้าสู่ระบบแบบไม่ใช้รหัสผ่านหากคุณกำลังตัดคำสั่งของระบบเช่น "ssh", "scp" หรือ "rsync" คุณสามารถฝังรหัสผ่านในสคริปต์โดยใช้ Paramiko หรือไลบรารีอื่น ๆ ได้ แต่คุณอาจพบว่าไม่มีเอกสารประกอบที่น่าหงุดหงิดโดยเฉพาะอย่างยิ่งหากคุณไม่คุ้นเคยกับพื้นฐานของการเชื่อมต่อ SSH (เช่น - การแลกเปลี่ยนคีย์ตัวแทน ฯลฯ ) อาจเป็นไปโดยไม่ได้บอกว่าคีย์ SSH มักจะเป็นความคิดที่ดีกว่ารหัสผ่านสำหรับสิ่งประเภทนี้
หมายเหตุ: มันยากที่จะเอาชนะ rsync หากคุณวางแผนที่จะถ่ายโอนไฟล์ผ่าน SSH โดยเฉพาะอย่างยิ่งถ้าทางเลือกอื่นคือ scp เก่าธรรมดา
ฉันใช้ Paramiko ด้วยความตั้งใจที่จะเปลี่ยนการโทรของระบบ แต่พบว่าตัวเองกลับไปใช้คำสั่งที่รวมไว้เนื่องจากใช้งานง่ายและคุ้นเคยในทันที คุณอาจจะแตกต่าง ฉันให้ Conch ครั้งหนึ่งเมื่อนานมาแล้ว แต่มันก็ไม่ถูกใจฉัน
หากเลือกใช้เส้นทางการเรียกระบบ Python จะมีอาร์เรย์ของตัวเลือกเช่น os.system หรือโมดูลคำสั่ง / กระบวนการย่อย ฉันจะใช้โมดูลกระบวนการย่อยถ้าใช้เวอร์ชัน 2.4+
พบปัญหาเดียวกัน แต่แทนที่จะ "แฮ็ก" หรือเลียนแบบบรรทัดคำสั่ง:
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')
คุณสามารถทำสิ่งนี้เพื่อจัดการการตรวจสอบคีย์โฮสต์ได้เช่นกัน
import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")
fabric
สามารถใช้เพื่ออัปโหลดไฟล์ 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()
คุณสามารถใช้แพ็คเกจข้าราชบริพารซึ่งออกแบบมาสำหรับสิ่งนี้
สิ่งที่คุณต้องมีคือติดตั้งข้าราชบริพารและทำ
from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()
นอกจากนี้ยังช่วยให้คุณสามารถรับรองความถูกต้องข้อมูลรับรองและไม่จำเป็นต้องพิมพ์ซ้ำแล้วซ้ำอีก
ฉันใช้sshfsเพื่อเมานต์ไดเร็กทอรีระยะไกลผ่าน ssh และปิดเพื่อคัดลอกไฟล์:
$ mkdir ~/sshmount
$ sshfs user@remotehost:/path/to/remote/dst ~/sshmount
จากนั้นใน python:
import shutil
shutil.copy('a.txt', '~/sshmount')
วิธีนี้มีข้อดีคือคุณสามารถสตรีมข้อมูลได้หากคุณกำลังสร้างข้อมูลแทนที่จะใช้แคชในเครื่องและส่งไฟล์ขนาดใหญ่ไฟล์เดียว
ลองทำเช่นนี้หากคุณไม่ต้องการใช้ใบรับรอง 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!')
การใช้ทรัพยากรภายนอก 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')
การเรียกใช้scp
คำสั่งผ่านกระบวนการย่อยไม่อนุญาตให้รับรายงานความคืบหน้าภายในสคริปต์ pexpect
สามารถใช้เพื่อดึงข้อมูลนั้น:
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})
แนวทางที่ง่ายมากมีดังต่อไปนี้:
import os
os.system('sshpass -p "password" scp user@host:/path/to/file ./')
ไม่จำเป็นต้องใช้ไลบรารี python (เฉพาะระบบปฏิบัติการ) และใช้งานได้อย่างไรก็ตามการใช้วิธีนี้ต้องอาศัยไคลเอ็นต์ ssh อื่นที่จะติดตั้ง ซึ่งอาจส่งผลให้เกิดพฤติกรรมที่ไม่ต้องการหากทำงานบนระบบอื่น
ประเภทของการแฮ็ก แต่สิ่งต่อไปนี้ควรใช้งานได้ :)
import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)