SCP 또는 SSH를 사용하여 Python에서 원격 서버에 파일을 복사하는 방법은 무엇입니까?


103

cron에서 실행되는 매일 Python 스크립트에 의해 생성되는 로컬 컴퓨터에 텍스트 파일이 있습니다.

SSH를 통해 해당 파일을 내 서버로 안전하게 전송하기 위해 약간의 코드를 추가하고 싶습니다.


1
비슷한 것을 발견, u는 표정 가질 수 stackoverflow.com/questions/11009308/...
— JJ84

답변:


55

당신은 호출 할 수 있습니다 scpbash는 명령 (이상은 파일을 복사 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블록)이므로 Python에서 디스크로 플러시된다는 것을 알 수 있습니다.

scp가 공개 ssh 키로 자동으로 인증되도록 (즉, 스크립트가 암호를 요청하지 않도록) ssh 키를 미리 생성하고 (소스 시스템에서) 설치 (대상 시스템에)해야합니다. .


3
@CharlesDuffy : subprocess.check_call(['scp', srcfile, dest])대신 Python 2.5부터 사용할 수 있습니다 .rc = Popen(..).wait(); if rc != 0: raise ..
— jfs

1
ssh 키를 추가하는 것은 필요하지 않을 때 좋은 해결책이 아닙니다. 예를 들어 일회성 통신이 필요한 경우 보안상의 이유로 ssh 키를 설정하지 않습니다.
— orezvani

150

Paramiko 라이브러리를 사용하여 Python에서이를 수행하려면 (즉, subprocess.Popen 또는 이와 유사한 것을 통해 scp를 래핑하지 않음) 다음과 같이하면됩니다.

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()

(알 수없는 호스트, 오류, 필요한 디렉토리 생성 등을 처리하고 싶을 것입니다.)


14
paramiko에는 멋진 sftp.put (self, localpath, remotepath, callback = None) 기능도 있으므로 쓰기를 열고 각 파일을 닫을 필요가 없습니다.
— Jim Carroll

6
SFTP는 SCP와 같지 않습니다.
— Drahkar

4
@Drahkar 질문은 SSH를 통해 파일을 보낼 것을 요청합니다. 그것이하는 일입니다.
— Tony Meyer

8
참고 :이 작업을 즉시 수행하려면 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())인스턴스화 후 추가하십시오 ssh.
— doda

1
여러분, 서버 비밀번호를 사용해도 안전합니까? 개인 키를 사용할 가능성이 있습니까?
— Aysennoussi

32

아마도 subprocess 모듈을 사용할 것입니다 . 이 같은:

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

어디 destination아마 양식입니다 user@remotehost:remotepath. 단일 문자열 인수를 사용하여 shell=True경로의 공백을 처리하지 않는 단일 문자열 인수를 사용하는 내 원래 답변의 약점을 지적한 @Charles Duffy에게 감사드립니다 .

모듈 설명서에는 이 작업과 함께 수행 할 수있는 오류 검사 예제가 있습니다.

머신간에 무인 암호없는 scp를 수행 할 수 있도록 적절한 자격 증명을 설정했는지 확인합니다 . 이것에 대한 stackoverflow 질문이 이미 있습니다.


3
subprocess.Popen 사용은 올바른 것입니다. 배열이 아닌 문자열을 전달 (그리고 shell = True 사용)하는 것은 잘못된 것입니다. 공백이있는 파일 이름이 올바르게 작동하지 않음을 의미하기 때문입니다.
— Charles Duffy

2
"sts = os.waitpid (p.pid, 0)"대신 "sts = p.wait ()"를 사용할 수 있습니다.
— db42

이 프로토콜에 대한 직접 Python API를 사용하여 실행이 자유로운 방법이 있습니까?
— lpapp

1
subprocess.check_call(['scp', myfile, destination])Python 2.5 (2006)부터 대신 사용할 수 있습니다
— jfs

@JFSebastian : 네, 12 월에 확인했습니다. 내 찬성 투표는 적어도 그것을 증명합니다. :) 그래도 후속 조치에 감사드립니다.
— lpapp

12

문제에 접근하는 방법에는 몇 가지가 있습니다.

  1. 명령 줄 프로그램 래핑
  2. SSH 기능을 제공하는 Python 라이브러리 (예 : Paramiko 또는 Twisted Conch )

각 접근 방식에는 고유 한 단점이 있습니다. "ssh", "scp"또는 "rsync"와 같은 시스템 명령을 래핑하는 경우 암호없는 로그인을 사용하려면 SSH 키를 설정해야합니다. Paramiko 또는 다른 라이브러리를 사용하여 스크립트에 암호를 포함 할 수 있지만 특히 SSH 연결의 기본 사항 (예 : 키 교환, 에이전트 등)에 익숙하지 않은 경우 문서가 부족하다는 것을 알 수 있습니다. SSH 키가 이런 종류의 암호보다 거의 항상 더 나은 아이디어라는 것은 말할 필요도 없습니다.

참고 : SSH를 통해 파일을 전송할 계획이라면 rsync를 이기기가 어렵습니다. 특히 대안이 일반 오래된 scp 인 경우 더욱 그렇습니다.

저는 Paramiko를 시스템 호출을 대체하기 위해 사용했지만 사용의 용이성과 즉각적인 친숙 함으로 인해 래핑 된 명령에 다시 끌 렸습니다. 당신은 다를 수 있습니다. 나는 얼마 전에 Conch를 한 번 줬지만 그것은 나에게 호소력이 없었습니다.

시스템 호출 경로를 선택하면 Python은 os.system 또는 명령 / 하위 프로세스 모듈과 같은 옵션 배열을 제공합니다. 버전 2.4 이상을 사용하는 경우 하위 프로세스 모듈을 사용합니다.


1
호기심 : rsync 대 scp에 대한 이야기는 무엇입니까?
— Alok

7

동일한 문제에 도달했지만 "해킹"또는 에뮬레이션 명령 줄 대신 :

여기 에서이 답변을 찾았 습니다 .

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

1
참고-이것은 scp 패키지에 의존합니다. 2017 년 12 월 현재 해당 패키지의 최신 업데이트는 2015 년이며 버전 번호는 0.1.x입니다. 이 종속성을 추가하고 싶은지 확실하지 않습니다.
— Chuck

2
그래서 지금은 2018 년이고 5 월에 0.11.0 버전을 출시했습니다. 그래서 SCPClient가 결국 죽지 않은 것 같습니까?
— Adriano_Pinaffo

5

이와 같은 작업을 수행하여 호스트 키 검사도 처리 할 수 ​​있습니다.

import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")

4

fabric 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()

2

이를 위해 정확히 설계된 vassal 패키지를 사용할 수 있습니다.

당신이 필요로하는 것은 가신을 설치하고

from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()

또한 인증 자격 증명을 저장하고 다시 입력 할 필요가 없습니다.


1

나는 sshfs 를 사용 하여 ssh를 통해 원격 디렉토리를 마운트하고 shutil 을 사용하여 파일을 복사했습니다.

$ mkdir ~/sshmount
$ sshfs user@remotehost:/path/to/remote/dst ~/sshmount

그런 다음 파이썬에서 :

import shutil
shutil.copy('a.txt', '~/sshmount')

이 방법은 로컬로 캐싱하고 하나의 큰 파일을 보내는 대신 데이터를 생성하는 경우 데이터를 스트리밍 할 수 있다는 장점이 있습니다.


1

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

1

외부 리소스 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')

0

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})

로컬 네트워크 (linux-> linux)의 python 복사 파일 참조


0

매우 간단한 접근 방식은 다음과 같습니다.

import os
os.system('sshpass -p "password" scp user@host:/path/to/file ./')

파이썬 라이브러리가 필요하지 않으며 (OS 만 해당) 작동하지만이 방법을 사용하면 설치할 다른 ssh 클라이언트에 의존합니다. 다른 시스템에서 실행하면 원하지 않는 동작이 발생할 수 있습니다.


-3

일종의 해킹이지만 다음은 작동합니다. :)

import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.