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.