Python 강좌 / 파일 검색·복사와 안전한 일괄 처리
파일 자동화는 대상 목록을 먼저 출력하는 dry-run과 실제 작업을 분리하는 방식으로 시작하는 것이 좋습니다. 이 실습은 지정 폴더의 텍스트 파일만 새 위치로 복사하고 기존 파일은 덮어쓰지 않습니다.
대상과 보존 정책 먼저 정하기
이 예제는 현재 실습 폴더 아래 copy-demo/in과 copy-demo/out만 사용하며 원본 삭제·이동은 하지 않습니다. 실행 전에 현재 폴더를 확인하고 기존 업무 폴더에서는 사용하지 마세요. 하위 폴더를 재귀 검색하지 않고 심볼릭 링크도 제외합니다.
관련 동작과 지원 조건은 Python: shutil에서 확인할 수 있습니다.
이동은 복사보다 원본 보존에 영향을 줄 수 있습니다. 먼저 복사·내용 검증·별도 정리 순서로 작업을 분리하면 실패 범위를 확인하기 쉽습니다. 이름 충돌이 있으면 자동 번호를 붙이기보다 오류로 멈추는 정책을 선택했습니다.
샘플 준비와 복사를 분리합니다. 먼저 빈 실습 폴더에서 아래 setup_copy.py를 한 번 실행해 copy-demo/in/note.txt를 만듭니다. 이 준비 단계는 파일을 생성하지만 copy 도구의 기본 dry-run은 경로만 보여 주며 copy-demo/out 폴더나 파일을 만들지 않습니다. --apply를 지정한 경우에만 복사하고 기존 대상이 있으면 중단합니다. 원본 이동이나 삭제는 하지 않는 복사 실습입니다.
아래 전체 코드를 setup_copy.py로 저장하고 같은 실습 폴더에서 python setup_copy.py로 실행합니다.
from pathlib import Path
source = Path("copy-demo/in")
source.mkdir(parents=True, exist_ok=True)
with (source / "note.txt").open("x", encoding="utf-8") as f:
f.write("demo\n")
print("sample ready")
실행 결과 sample ready를 확인한 뒤 safe_copy.py를 실행합니다.
대상 경로와 실패 처리은 pathlib로 파일·폴더 경로 다루기 및 예외 처리하기: try·except·else·finally·raise에서 사용하는 규칙과 연결됩니다.
전체 예제와 실행 결과
별도의 실습 폴더에서 아래 파일을 UTF-8로 저장합니다. 가상환경을 선택한 터미널에서 python safe_copy.py 명령으로 실행합니다. macOS·Ubuntu에서 가상환경을 사용하지 않는 경우 명령 이름을 python3으로 바꿉니다.
safe_copy.py 파일의 전체 내용입니다.
import argparse
import hashlib
import shutil
from pathlib import Path
parser = argparse.ArgumentParser()
parser.add_argument("--apply", action="store_true")
args = parser.parse_args()
source, target = Path("copy-demo/in"), Path("copy-demo/out")
if not source.is_dir():
parser.error("prepare copy-demo/in first")
files = sorted(p for p in source.glob("*.txt") if p.is_file() and not p.is_symlink())
for src in files:
dst = target / src.name
if dst.exists() or dst.is_symlink():
raise FileExistsError(dst)
print("copy", src.name)
if args.apply:
target.mkdir(parents=True, exist_ok=True)
with src.open("rb") as incoming, dst.open("xb") as outgoing:
shutil.copyfileobj(incoming, outgoing)
if hashlib.sha256(src.read_bytes()).digest() != hashlib.sha256(dst.read_bytes()).digest():
raise RuntimeError("copy verification failed")
print("applied" if args.apply else "dry-run")
예상 결과는 다음과 같습니다.
copy note.txt
dry-run
실제 복사와 결과 검사
dry-run으로 표시된 목록을 확인한 뒤 같은 파일을 --apply로 실행합니다. xb 모드는 대상이 이미 있으면 생성을 거부합니다. 복사 뒤 SHA-256 해시를 비교해 예제 파일의 바이트가 같은지 확인합니다.
python safe_copy.py --apply
처음 실행하면 copy note.txt와 applied가 출력되고 copy-demo/out/note.txt가 생깁니다. 같은 대상에 재실행하면 FileExistsError로 멈추며 기존 결과를 덮어쓰지 않아야 합니다. 부분 복사 파일이 남으면 경로와 내용을 확인한 뒤 재시도 정책을 정합니다.
실무에서 확장할 때의 한계
예제의 해시 계산은 작은 파일 전체를 메모리에 읽습니다. 큰 파일은 블록 단위로 읽어 계산합니다. 파일 내용이 같아도 메타데이터·접근 제어·심볼릭 링크 정책까지 모두 검증되는 것은 아닙니다. 신뢰할 수 없는 사용자가 대상 디렉터리를 동시에 바꾸는 상황까지 보호하는 예제도 아닙니다.
이름 변경이나 삭제를 추가하려면 변경 전 목록, 충돌 검사, 백업과 복구 절차를 먼저 설계합니다. 실습을 끝내고 정리할 때도 실제 절대 경로가 방금 만든 copy-demo 안인지 확인하고 필요한 결과만 지웁니다.




