윈도우, 리눅스 모두 사용 가능합니다.
(저는 라즈베리파이에 사용했습니다.)
다들 이방법은 아실꺼라고 믿습니다.
검색하면 많이 나오는 방식입니다..
import shutil
if __name__ == '__main__':
shutil.copytree(
'/home/pi/LOG',
'/media/pi/USB/LOG',
)
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
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
146MB 폴더를 같은 작업 100번 돌려본 평균값 결과 입니다.
(PC에 따라 성능은 상의 할수 있습니다.)
직접 해보고 싶으신분들을 위해 압축해서 올려 봅니다.
'복사 대상 위치', '붙여넣기할 대상 위치' 변경후 사용하시기 바랍니다.
성능 순위 : 직접 만들어서 > shutil.copytree > copy_tree