파이썬에서 파일 비교하기
파이썬 표준 라이브러리의 filecmp 모듈을 사용하면 파일과 디렉터리의 내용을 효율적으로 비교할 수 있습니다. 이 모듈은 메타데이터 기반의 빠른 비교부터 바이트 단위의 정밀 비교, 디렉터리 전체의 재귀적 비교까지 다양한 기능을 제공합니다.
주요 함수와 클래스
1. filecmp.cmp() - 단일 파일 비교
cmp(file1, file2, shallow=True)
두 파일을 비교하여 동일하면 True, 다르면 False를 반환합니다.
- shallow=True (기본값):
os.stat()이 반환하는 메타데이터(크기, 수정 시간 등)만 비교합니다. 내용이 달라도 메타데이터가 같으면 동일하다고 판단합니다. - shallow=False: 파일 실제 내용을 바이트 단위로 비교합니다. 느리지만 정확합니다.
2. filecmp.cmpfiles() - 디렉터리 내 여러 파일 일괄 비교
cmpfiles(dir1, dir2, common, shallow=True)
두 디렉터리에 공통으로 존재하는 파일 목록(common)을 비교하여 세 가지 리스트를 튜플로 반환합니다.
match: 내용이 동일한 파일 목록mismatch: 내용이 다른 파일 목록errors: 비교 실패한 파일 목록(권한 없음, 존재하지 않음 등)
3. filecmp.dircmp() - 디렉터리 종합 비교 객체
dircmp(dir1, dir2, ignore=None, hide=None)
두 디렉터리를 비교하는 객체를 생성합니다. 다양한 속성과 메서드로 상세 분석이 가능합니다.
주요 속성
left_list,right_list: 각 디렉터리의 파일/서브디렉터리 목록common: 양쪽에 모두 존재하는 항목left_only,right_only: 한쪽만 존재하는 항목common_files,common_dirs: 공통 파일/서브디렉터리same_files,diff_files,funny_files: 내용 동일/상이/비교 불가 파일subdirs: 공통 서브디렉터리명을 키로 하는dircmp객체 딕셔너리(재귀 비교용)
리포트 메서드
report(): 현재 디렉터리 비교 요약 출력report_partial_closure(): 직계 서브디렉터리까지 포함하여 출력report_full_closure(): 모든 하위 디렉터리 재귀적으로 비교 출력
실전 예제
테스트 데이터 준비
import os
import filecmp
def setup_test_environment(base_dir='example'):
"""비교 테스트용 디렉터리 구조와 파일 생성"""
if os.path.exists(base_dir):
import shutil
shutil.rmtree(base_dir)
os.makedirs(f'{base_dir}/dir1/common_dir')
os.makedirs(f'{base_dir}/dir2/common_dir')
os.makedirs(f'{base_dir}/dir1/dir_only_in_dir1')
os.makedirs(f'{base_dir}/dir2/dir_only_in_dir2')
# 동일한 내용의 파일
with open(f'{base_dir}/dir1/common_file', 'w') as f:
f.write('Hello, Writing Same Content')
with open(f'{base_dir}/dir2/common_file', 'w') as f:
f.write('Hello, Writing Same Content')
# 다른 내용의 파일 (같은 이름)
with open(f'{base_dir}/dir1/not_the_same', 'w') as f:
f.write('Content in dir1')
with open(f'{base_dir}/dir2/not_the_same', 'w') as f:
f.write('Content in dir2')
# 한쪽만 존재하는 파일
with open(f'{base_dir}/dir1/file_only_in_dir1', 'w') as f:
f.write('Only in dir1')
with open(f'{base_dir}/dir2/file_only_in_dir2', 'w') as f:
f.write('Only in dir2')
# 타입이 다른 동명 항목 (파일 vs 디렉터리)
with open(f'{base_dir}/dir1/file_in_dir1', 'w') as f:
f.write('This is a file in dir1')
os.makedirs(f'{base_dir}/dir2/file_in_dir1', exist_ok=True)
if __name__ == '__main__':
setup_test_environment()
파일 단위 비교 (cmp)
import filecmp
print('=== filecmp.cmp() 예제 ===')
# 동일한 내용의 파일
print('common_file (shallow=True):',
filecmp.cmp('example/dir1/common_file', 'example/dir2/common_file'))
print('common_file (shallow=False):',
filecmp.cmp('example/dir1/common_file', 'example/dir2/common_file', shallow=False))
# 다른 내용의 파일
print('not_the_same (shallow=True):',
filecmp.cmp('example/dir1/not_the_same', 'example/dir2/not_the_same'))
print('not_the_same (shallow=False):',
filecmp.cmp('example/dir1/not_the_same', 'example/dir2/not_the_same', shallow=False))
# 자기 자신과 비교
print('file_only_in_dir1 (자기 자신):',
filecmp.cmp('example/dir1/file_only_in_dir1', 'example/dir1/file_only_in_dir1'))
=== filecmp.cmp() 예제 ===
common_file (shallow=True): True
common_file (shallow=False): True
not_the_same (shallow=True): False
not_the_same (shallow=False): False
file_only_in_dir1 (자기 자신): True
디렉터리 내 공통 파일 일괄 비교 (cmpfiles)
import os
import filecmp
# 양쪽 디렉터리에 공통으로 존재하는 일반 파일만 추출
dir1_files = {f for f in os.listdir('example/dir1')
if os.path.isfile(os.path.join('example/dir1', f))}
dir2_files = {f for f in os.listdir('example/dir2')
if os.path.isfile(os.path.join('example/dir2', f))}
common_files = list(dir1_files & dir2_files)
print(f'공통 파일: {common_files}')
match, mismatch, errors = filecmp.cmpfiles(
'example/dir1', 'example/dir2', common_files, shallow=False
)
print(f'일치: {match}')
print(f'불일치: {mismatch}')
print(f'오류: {errors}')
공통 파일: ['common_file', 'not_the_same', 'file_in_dir1']
일치: ['common_file']
불일치: ['not_the_same', 'file_in_dir1']
오류: []
참고: file_in_dir1은 한쪽은 파일, 다른 쪽은 디렉터리라 비교 불가하여 mismatch에 포함됩니다.
디렉터리 종합 비교 (dircmp)
import filecmp
dc = filecmp.dircmp('example/dir1', 'example/dir2')
print('=== 기본 리포트 ===')
dc.report()
print('\n=== 전체 재귀 리포트 ===')
dc.report_full_closure()
print('\n=== 주요 속성 직접 접근 ===')
print(f'dir1만 있는 항목: {dc.left_only}')
print(f'dir2만 있는 항목: {dc.right_only}')
print(f'공통 파일: {dc.common_files}')
print(f'내용 동일 파일: {dc.same_files}')
print(f'내용 다른 파일: {dc.diff_files}')
print(f'비교 불가 항목: {dc.funny_files}')
print(f'공통 서브디렉터리: {dc.common_dirs}')
=== 기본 리포트 ===
diff example/dir1 example/dir2
Only in example/dir1 : ['dir_only_in_dir1', 'file_only_in_dir1']
Only in example/dir2 : ['dir_only_in_dir2', 'file_only_in_dir2']
Identical files : ['common_file']
Differing files : ['not_the_same']
Common subdirectories : ['common_dir']
Common funny cases : ['file_in_dir1']
=== 전체 재귀 리포트 ===
diff example/dir1 example/dir2
Only in example/dir1 : ['dir_only_in_dir1', 'file_only_in_dir1']
Only in example/dir2 : ['dir_only_in_dir2', 'file_only_in_dir2']
Identical files : ['common_file']
Differing files : ['not_the_same']
Common subdirectories : ['common_dir']
Common funny cases : ['file_in_dir1']
diff example/dir1/common_dir example/dir2/common_dir
Common subdirectories : []
=== 주요 속성 직접 접근 ===
dir1만 있는 항목: ['dir_only_in_dir1', 'file_only_in_dir1']
dir2만 있는 항목: ['dir_only_in_dir2', 'file_only_in_dir2']
공통 파일: ['common_file', 'not_the_same', 'file_in_dir1']
내용 동일 파일: ['common_file']
내용 다른 파일: ['not_the_same']
비교 불가 항목: ['file_in_dir1']
공통 서브디렉터리: ['common_dir']
활용 팁과 주의사항
- 성능 vs 정확도: 대용량 파일 비교 시
shallow=True로 1차 필터링 후, 후보만shallow=False로 정밀 비교하면 효율적입니다. - 바이너리 파일: 텍스트/바이너리 구분 없이 바이트 단위로 비교하므로 모든 파일 유형에 적용 가능합니다.
- 심볼릭 링크: 링크 자체보다 링크 대상 파일을 비교합니다.
- 무시/숨김 목록 커스터마이징:
dircmp(ignore=['*.pyc', '__pycache__'], hide=['.git'])등으로 불필요한 항목 제외 가능. - 재귀 비교 직접 구현:
subdirs딕셔너리를 순회하며 커스텀 로직 적용 가능.
요약
filecmp 모듈은 별도 설치 없이 파이썬 표준 라이브러리로 파일과 디렉터리를 비교할 수 있는 강력한 도구입니다. 단순 파일 비교부터 복잡한 디렉터리 동기화 검증까지, 용도에 맞게 cmp, cmpfiles, dircmp를 조합해 사용하세요.