공유 블로그

윈도우, 리눅스 모두 사용 가능합니다.

(저는 라즈베리파이에 사용했습니다.)

 

첫번째 방법(shutil.copytree)

다들 이방법은 아실꺼라고 믿습니다.

검색하면 많이 나오는 방식입니다..

import shutil

if __name__ == '__main__':
    shutil.copytree(
        '/home/pi/LOG',
        '/media/pi/USB/LOG',
    )

shutil.copytree('복사 대상 위치', '붙여넣기할 대상 위치')

shutil.copytree 에러 날시

하지만 '붙여넣기할 대상 위치'에 같은 폴더가 존재할 경우 에러가 납니다.

에러 내용 : FileExistsError: [Errno 17] File exists: '~ 붙여넣기 경로... ~'

shutil.copytree에서 오류 해결방법은 검색하면 안나오는거 같더라구요.

그래서 공식 홈페이지에서 찾아봤더니 방법이 있습니다.

import shutil

if __name__ == '__main__':
    shutil.copytree(
        '/home/pi/LOG',
        '/media/pi/USB/LOG',
        dirs_exist_ok=True
    )

shutil.copytree('복사 대상 위치', '붙여넣기할 대상 위치',dirs_exist_ok=True[기본값:False])

'dirs_exist_ok=True'로 변경 해주면 된다고 합니다.

[python 3.8버전에 추가됨]

출처 : https://docs.python.org/ko/3/library/shutil.html


두번째 방법(copy_tree)

from distutils.dir_util import copy_tree

if __name__ == '__main__':
    copy_tree(
    	'/home/pi/LOG',
        '/media/pi/USB/LOG'
    )

copy_tree('복사 대상 위치', '붙여넣기할 대상 위치')


세번째 방법(직접 만들어서)

다 만들고 나서 혹시나 shutil 공식 홈페이지에 폴더 있을시 무시하고 복사 할수 있는지 보니까 있더라구요;;[위 내용 참조]

(괜히 힘들게 만들었네요...ㅜㅜ;; 그리고 copy_tree라는 함수도 같이 나오더라구요;;)

째든 혹시라도 복사 중간에 작업 또는 코드를 추가 해야될수도 있기 때문에 한번 올려봅니다.

from __future__ import annotations

import os
import math
import time
import shutil
from pathlib import Path


############################
# 문자열 개수 세기(한글, 다른 나라언어 : +2 | 영어, 나머지 : +1 | 탭 +1)
def stringCountLine(string: str, details=False, DEBUG=False):
    if details:
        record = {}

    def _stringCount(string: str):
        count = 0
        for character in string:
            # print(count, character, character.isalpha())

            # 영어 판단
            if 'a' <= character <= "z" or 'A' <= character <= 'Z':
                count += 1
            elif '\t' in character:
                count += 4
            # 나머지 문자(한글, 다른 나라언어 등)
            elif character.isalpha():
                count += 2
            # 진짜 나머지 문자(숫자, 특수문자 등)
            else:
                count += 1

        if details:
            record[string] = count

        if DEBUG:
            print(f'{string} -> {count}')

        return count

    if '\n' in string:
        string = max(string.split('\n'), key=_stringCount)

    if details:
        return _stringCount(string), record

    return _stringCount(string)


# 제목 구분선 출력
def printDivideTitle(titleName: str, dividingLine: str = '-', lineCount: int = 0):
    if type(lineCount) is not int:
        return False
    #             제목 전체 길이, 구분선 전체 길이
    totalLength = len(titleName)+(lineCount*2)
    return titleName.center(totalLength, dividingLine)


# 제목 구분선 길이 계산
def makeDividingLine(titleName: str, totalContentLength: int, dividingLine: str = '-'):
    if type(totalContentLength) is str:
        totalContentLength = stringCountLine(totalContentLength)

    # 내용 개수 세기
    titleLength = stringCountLine(titleName)

    # 내용보다 제목이 길 경우
    if titleLength > totalContentLength:
        calc = titleLength - totalContentLength
    else:
        calc = totalContentLength - titleLength

    # 구분선 길이 계산
    drew_Divding_totalLen = math.ceil(calc/2)

    # 최종 출력내용
    returnData = printDivideTitle(
        titleName, dividingLine, drew_Divding_totalLen
    )

    # 제목에 구분선 출력, 구분선만의 개수(반쪽), 구분선 제외 제목 길이(한글 포함시 +2 -> stringCountLine함수 참조)
    return returnData, drew_Divding_totalLen, titleLength


# 제목 기준 마지막 구분선 출력
def endDividingLine(title, dividingLine: str = '-'):
    return dividingLine*stringCountLine(title)


# 최종 결과물 출력
def dividingAfter(titleName: str, realContent: str, drawDividing: str = '-', showWarning: bool = True, showDetailsValue=False):
    # 내용 개수 세기
    realContentCount, details = stringCountLine(
        realContent, details=True)

    # 제목 구분선 출력
    dividingLine = makeDividingLine(
        titleName, realContentCount, drawDividing
    )

    def returnData(showValue=False) -> (str | dict):
        for k, v in details.items():
            if v:
                total = realContent.replace(k, f'{dividingLine[0]}\n{k}')
                break

        text = f'{total}\n{endDividingLine(dividingLine[0], drawDividing)}\n'
        if showValue:
            details_Value = {
                'text': text,
                'Only_Dividing_Count(half)': dividingLine[1],
                'Console_Title_Length': dividingLine[2],
                'Draw_Dividing': drawDividing
            }
            return details_Value
        else:
            return text

    # 구분선 제외 제목 길이가 내용보다 클경우
    if dividingLine[2] > realContentCount:
        if showWarning:
            return False
        else:
            # 이부분 주석을 해제 하시면 정상적으로 출력하는 것을 볼수 있습니다.
            return returnData(showDetailsValue)

    return returnData(showDetailsValue)
############################


# 폴더 없을시 폴더 생성
def createDirectory(directory: str):
    try:
        name = Path(directory).name

        if not os.path.exists(directory):
            os.makedirs(directory)

            print(
                f"createDirectory: '{name}' Success To Make Directory File!! [Location -> {directory}]"
            )
        return name

    # 권한이 없을 경우
    except PermissionError as e:
        content = 'sudo 또는 관리자 권한으로 실행 해주십시오...\n\n에러 내용 : {}'.format(e)
        print(dividingAfter(' 권한이 없습니다. ', content))

        return False

    # 형식에 안맞을 경우
    except SyntaxError as e:
        print("{} 형식이 잘못되었습니다.".format(directory))
        print('SyntaxError : {}'.format(e))

    # 폴더 있을 경우
    except OSError:
        print(
            "createDirectory: '{}' Failed To Create The Directory...".format(
                name
            )
        )
        return name


# 폴더 통째로 복사
def folder_file_copy(copyPath: str, pastePath: str):
    file_dir = os.path.dirname(os.path.join(copyPath, ''))

    # 폴더에 있는 모든 파일 또는 폴더 가져옴
    for i, (path, _, files) in enumerate(os.walk(file_dir)):
        copyPath = path.split(file_dir)[-1].lstrip('/').lstrip('\\')
        # copyPath = Path(path).name
        # print(i, files, path, copyPath, _)

        # 폴더 생성
        if copyPath:
            returnDATA = createDirectory(
                os.path.join(pastePath, copyPath)
            )
            print('폴더 이름 :', returnDATA)

        # 파일 복사
        for j, file in enumerate(files):
            j += 1  # 보여주기 위함
            file_path = os.path.join(path, file)
            # 폴더안 폴더가 있을 경우
            if copyPath:
                dest_path = os.path.join(pastePath, copyPath, file)
            else:
                dest_path = os.path.join(pastePath, file)

            # 파일 복사
            shutil.copy(file_path, dest_path)

            print(
                '\t[{}_{}번째 파일 복사 완료 : {} -> {}]'.format(
                    i,
                    j,
                    file_path,
                    dest_path
                )
            )


# 진짜 복사(복사에 대한 정보 출력)
def REAL_COPY_ALL(copyPath: str, pastePath: str, waitTime: int = 10):
    # 복사 대상 형식 체크
    try:
        copyPath = os.path.join(copyPath)
    except TypeError as e:
        content = "'{}' 형식이 잘못되었습니다.\n문자열 타입으로 입력해 주십시오...\n\n에러 내용 : {}".format(
            copyPath, e
        )
        print(dividingAfter(' 형식이 잘못되었습니다. ', content))
        return False

    # 붙여넣기 대상 형식 체크
    try:
        pastePath = os.path.join(pastePath)
    except TypeError as e:
        content = "'{}' 형식이 잘못되었습니다.\n문자열 타입으로 입력해 주십시오...\n\n에러 내용 : {}".format(
            pastePath, e
        )
        print(dividingAfter(' 형식이 잘못되었습니다. ', content))
        return False

    print('파일 복사 확인 : {} -> {}'.format(copyPath, pastePath))

    # 복사전 대기
    if waitTime:
        print('{}초후에 복사 시작 합니다...\n'.format(waitTime))
        time.sleep(waitTime)

    # 붙여넣기 대상에 하위 폴더가 없을 경우
    if createDirectory(pastePath) is False:
        return False
    else:
        name = Path(pastePath).name
        print('폴더 이름 :', name)

    # 폴더, 파일 모두 복사
    folder_file_copy(copyPath, pastePath)

    print('복사 완료')
    return True


if __name__ == '__main__':
    REAL_COPY_ALL(
        'D:/Users/USER/Downloads/1.Android WIFI & USB Mirroring 최종',
        'D:/Users/USER/Downloads/복사/TEST',
        waitTime=1
    )

REAL_COPY_ALL('복사 대상 위치', '붙여넣기할 대상 위치', 복사전 기다릴 시간]

 

결과

 

참고 : https://all-share-source-code.tistory.com/70

https://gentlesark.tistory.com/90

https://goodthings4me.tistory.com/840

 

python 내용에 맞게 구분선 출력 함수 제작

뭐라고 설명하기 어렵네여;;( 제목 짓기 어려웠습니다.ㅎ;; ) 일단 아래 결과물로 보시면 바로 이해 가실 겁니다. 결과물 위와 같이 내용의 개수를 세서 자동적으로 구분선을 만들어 깔끔하게 출

all-share-source-code.tistory.com

 

성능 비교

146MB 폴더를 같은 작업 100번 돌려본 평균값 결과 입니다.

(PC에 따라 성능은 상의 할수 있습니다.)

직접 해보고 싶으신분들을 위해 압축해서 올려 봅니다.

'복사 대상 위치', '붙여넣기할 대상 위치' 변경후 사용하시기 바랍니다.

 

세번째 방법(직접 만들어서)

두번째 방법(copy_tree)

첫번째 방법(shutil.copytree)

성능 순위 : 직접 만들어서 > shutil.copytree > copy_tree

성능 비교.zip
0.00MB

 

공유하기

facebook twitter kakaoTalk naver band kakaostory Copy URL