ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] pathlib: OS 독립적인 파일·디렉터리 경로 다루기
    업무 자동화/Python 2025. 11. 27. 22:00

    1. pathlib란 무엇인가?

    1-1. 정의

    • pathlib파일 시스템 경로를 객체 지향적으로 다루는 표준 라이브러리입니다.
    • 기존의 문자열 기반 os.path 대신, Path 객체를 중심으로 경로를 처리합니다.
    from pathlib import Path
    
    p = Path("data") / "input" / "file.csv"
    print(p)           # OS에 맞는 경로 문자열로 표시

    1-2. 왜 pathlib를 써야 할까?

    기존 방식 (os, os.path) 예:

    import os
    
    base = "data"
    path = os.path.join(base, "input", "file.csv")
    if os.path.exists(path):
        ...

    pathlib 사용:

    from pathlib import Path
    
    base = Path("data")
    path = base / "input" / "file.csv"
    if path.exists():
        ...

    장점

    • / 연산자로 경로를 직관적으로 결합 (연산자 오버로딩)
    • Windows C:\... / Linux /home/...OS별 경로 구분자 자동 처리
    • 메서드 이름이 직관적: exists(), is_file(), mkdir(), read_text()
    • 코드가 전체적으로 짧고 읽기 쉬운 스타일로 바뀜

     

    2. Path 객체 기본

    2-1. Path 생성

    from pathlib import Path
    
    # 현재 디렉터리 기준 상대 경로
    p1 = Path("data/input/file.csv")
    
    # 절대 경로
    p2 = Path("/var/log/syslog")           # Linux 스타일
    p3 = Path("C:/Users/user/Desktop")     # Windows 스타일도 그대로 가능
    
    # 현재 작업 디렉터리
    cwd = Path.cwd()
    print(cwd)

    2-2. 경로 결합

    from pathlib import Path
    
    base = Path("data")
    p = base / "input" / "2025" / "report.xlsx"
    print(p)  # OS에 맞는 경로 문자열로 출력
    • Path("data") / "input"os.path.join("data", "input") 과 동일한 효과입니다.

    2-3. 경로 요소 확인

    from pathlib import Path
    
    p = Path("data/input/report_2025.xlsx")
    
    print(p.name)       # 파일 이름 전체: 'report_2025.xlsx'
    print(p.stem)       # 확장자 제외 이름: 'report_2025'
    print(p.suffix)     # 확장자: '.xlsx'
    print(p.suffixes)   # 복수 확장자: ['.tar', '.gz'] 같은 경우
    print(p.parent)     # 상위 디렉터리: 'data/input'
    print(p.anchor)     # 루트 부분 ('C:\\', '/' 등)

     

    3. 파일/디렉터리 존재 여부, 타입 확인

    from pathlib import Path
    
    p = Path("data/input/report_2025.xlsx")
    
    print(p.exists())   # 존재 여부 (True/False)
    print(p.is_file())  # 파일인지?
    print(p.is_dir())   # 디렉터리인지?

    디렉터리 전체를 순회하면서 파일만 처리할 수도 있습니다.

    data_dir = Path("data/input")
    
    for path in data_dir.iterdir():
        if path.is_file():
            print("파일:", path.name)
        elif path.is_dir():
            print("폴더:", path.name)

     

    4. 디렉터리 생성/삭제, 파일 삭제/이동/이름 변경

    4-1. 디렉터리 생성

    from pathlib import Path
    
    target_dir = Path("output/reports/2025")
    
    # 상위 디렉터리가 없어도 한 번에 생성
    target_dir.mkdir(parents=True, exist_ok=True)
    • parents=True: 중간 경로까지 모두 생성
    • exist_ok=True: 이미 있어도 에러 내지 않음

    4-2. 파일 삭제

    from pathlib import Path
    
    p = Path("output/old_result.csv")
    
    if p.exists():
        p.unlink()      # 파일 삭제

    4-3. 디렉터리 삭제

    from pathlib import Path
    
    d = Path("output/tmp")
    
    if d.exists() and d.is_dir():
        d.rmdir()       # 비어 있는 디렉터리만 삭제 가능

    디렉터리 안에 파일이 있을 경우, shutil.rmtree()와 함께 쓰는 패턴을 권장합니다.

    from pathlib import Path
    import shutil
    
    d = Path("output/tmp")
    if d.exists():
        shutil.rmtree(d)    # 폴더 통째 삭제 (주의)

    4-4. 이름 변경 / 이동

    from pathlib import Path
    
    p = Path("data/input/raw.csv")
    
    # 같은 디렉터리 안에서 이름만 변경
    p_renamed = p.rename("data/input/raw_backup.csv")
    
    # 다른 디렉터리로 이동 + 이름 변경
    dest = Path("archive/2025/raw.csv")
    dest.parent.mkdir(parents=True, exist_ok=True)
    p_renamed.rename(dest)

     

    5. 파일 읽기/쓰기 (텍스트/바이너리)

    5-1. 텍스트 파일 읽기/쓰기

    from pathlib import Path
    
    p = Path("notes/todo.txt")
    
    # 전체 텍스트 읽기
    text = p.read_text(encoding="utf-8")
    print(text)
    
    # 텍스트 쓰기 (있으면 덮어씀)
    p.write_text("새로운 TODO 내용입니다.", encoding="utf-8")
    • 간단한 텍스트 파일 처리에는 open()보다 read_text/write_text가 더 간결합니다.

    5-2. 바이너리 읽기/쓰기

    from pathlib import Path
    
    p = Path("images/logo.png")
    
    data = p.read_bytes()      # 바이너리 읽기
    print(len(data))
    
    # 그대로 복사
    p_copy = Path("images/logo_copy.png")
    p_copy.write_bytes(data)

    5-3. open()과 함께 쓰기 (표준 패턴)

    파일을 순차적으로 읽거나, 줄 단위 처리할 땐 open()과 함께 쓰기도 합니다.

    from pathlib import Path
    
    p = Path("logs/app.log")
    
    with p.open("r", encoding="utf-8") as f:
        for line in f:
            if "ERROR" in line:
                print(line.rstrip())

     

    6. glob / rglob 을 이용한 패턴 매칭

    6-1. 특정 확장자 파일 찾기

    from pathlib import Path
    
    data_dir = Path("data/input")
    
    # data/input/*.csv
    for csv_file in data_dir.glob("*.csv"):
        print(csv_file.name)

    6-2. 하위 디렉터리까지 모두 탐색 (rglob)

    from pathlib import Path
    
    root = Path("data")
    
    # data/**/**/*.log (하위 폴더 포함 모든 .log)
    for log_file in root.rglob("*.log"):
        print(log_file)
    • glob: 현재 디렉터리 1단계만
    • rglob: 재귀적으로 모든 하위 디렉터리까지

    6-3. 여러 패턴 조합 (간단 예시)

    from pathlib import Path
    
    root = Path("data")
    
    csv_files = list(root.rglob("*.csv"))
    xlsx_files = list(root.rglob("*.xlsx"))
    
    print("CSV:", len(csv_files), "개")
    print("XLSX:", len(xlsx_files), "개")

     

    7. 실무에서 자주 쓰는 패턴 예시

    7-1. 오늘 날짜 기준 폴더 생성 후 결과 저장

    from pathlib import Path
    from datetime import date
    
    today = date.today().strftime("%Y%m%d")
    base_dir = Path("output") / today
    
    base_dir.mkdir(parents=True, exist_ok=True)
    
    result_path = base_dir / "summary.txt"
    result_path.write_text("오늘 리포트 요약 내용...", encoding="utf-8")
    
    print("저장 위치:", result_path)

    7-2. 특정 디렉터리에서 오래된 로그 삭제

    from pathlib import Path
    from datetime import datetime, timedelta
    
    log_dir = Path("logs")
    threshold = datetime.now() - timedelta(days=7)
    
    for log_file in log_dir.glob("*.log"):
        mtime = datetime.fromtimestamp(log_file.stat().st_mtime)
        if mtime < threshold:
            print("삭제:", log_file)
            log_file.unlink()

    7-3. 모든 CSV 파일을 읽어 이름과 크기 출력

    from pathlib import Path
    
    data_dir = Path("data")
    
    for csv_file in data_dir.rglob("*.csv"):
        size_kb = csv_file.stat().st_size / 1024
        print(f"{csv_file} \t {size_kb:.1f} KB")

     

    8. os.path과의 비교 요약

    항목 os.path 문자열 기반 pathlib (Path 객체)
    경로 타입 단순 문자열(str) Path 객체
    경로 결합 os.path.join(a, b, c) Path(a) / b / c
    구분자 처리 \\ vs / 실수하기 쉬움 OS에 맞게 자동 처리
    파일 정보 os.path.exists, os.path.isdir, os.stat p.exists(), p.is_dir(), p.stat()
    파일 읽기/쓰기 open(path) p.open(), p.read_text(), p.write_text()
    가독성 함수/모듈 중첩이 많아 길어지기 쉽습니다 메서드 체인으로 비교적 직관적

    새로운 프로젝트나 스크립트를 작성할 때는
    처음부터 pathlib 기반으로 경로를 설계하는 것을 강력 추천합니다.

     

    9. 정리

    • pathlib파일/폴더 경로를 객체로 다루는 표준 라이브러리
    • 주요 포인트
      • Path 객체로 경로를 표현 (Path("data") / "input" / "file.csv")
      • OS별 경로 구분자 자동 처리
      • 파일/디렉터리 조작 메서드: exists(), is_file(), mkdir(), unlink(), rename()
      • 파일 읽기/쓰기: read_text, write_text, read_bytes, write_bytes
      • 패턴 검색: glob, rglob로 확장자/패턴 기반 탐색
    • 기존 os.path 기반 코드를 점진적으로 pathlib로 옮기면
      • 가독성, 유지보수성, OS 호환성이 모두 좋아집니다.
Designed by Tistory.