파일 및 디렉터리 경로 처리
실용적인 파일 시스템 작업에 대한 심층적인 내용은 블로그 게시물을 확인하세요: 모든 개발자가 알아야 할 10 가지 필수 파일 시스템 작업.
Python 에서 경로 조작을 다루는 두 가지 주요 모듈이 있습니다. 하나는 os.path 모듈이고 다른 하나는 pathlib 모듈입니다.
Pathlib 대 OS 모듈
pathlib은 파일 이름 가져오기, 파일 확장자 가져오기, 파일을 수동으로 열지 않고 읽기/쓰기와 같이 위에 나열된 것보다 훨씬 더 많은 기능을 제공합니다. 더 자세히 알고 싶다면 공식 문서를 참조하십시오.
Linux 및 Windows 경로
Windows 에서는 폴더 이름 구분 기호로 백슬래시 (\) 를 사용하여 경로를 작성합니다. macOS, Linux 및 BSD 와 같은 Unix 기반 운영 체제에서는 경로 구분 기호로 슬래시 (/) 를 사용합니다. 코드가 다른 플랫폼에서 작동해야 하는 경우 경로를 연결하는 것이 까다로울 수 있습니다.
다행히도 Python 의 pathlib 모듈은 이를 처리하는 쉬운 방법을 제공합니다.
*nix 에서 pathlib 사용:
# pathlib.Path: 크로스 플랫폼 경로 처리
from pathlib import Path
print(Path('usr').joinpath('bin').joinpath('spam')) # 경로 구성 요소 결합
usr/bin/spam
pathlib은 / 연산자를 사용하여 joinpath 에 대한 바로 가기를 제공합니다:
# Path 연산자 (/): 경로를 결합하는 편리한 방법 (크로스 플랫폼)
from pathlib import Path
print(Path('usr') / 'bin' / 'spam') # joinpath() 대신 / 연산자 사용
usr/bin/spam
경로 구분 기호가 Windows 와 Unix 기반 운영 체제 간에 다르다는 점에 유의하십시오. 이것이 바로 문자열을 연결하여 경로를 결합하는 대신 pathlib을 사용하려는 이유입니다.
로그인하여 이 퀴즈에 답하고 학습 진행 상황을 추적하세요
Path('usr') + 'bin' + 'spam'Path('usr') / 'bin' / 'spam'Path('usr').join('bin').join('spam')Path('usr/bin/spam')경로를 결합하는 것은 동일한 디렉터리 아래에 다른 파일 경로를 생성해야 할 때 유용합니다.
*nix 에서 pathlib 사용:
# Path.home(): 사용자의 홈 디렉터리를 가져와 파일 이름과 결합
my_files = ['accounts.txt', 'details.csv', 'invite.docx']
home = Path.home() # 홈 디렉터리 경로 가져오기
for filename in my_files:
print(home / filename) # 홈 경로를 각 파일 이름과 결합
/home/labex/project/accounts.txt
/home/labex/project/details.csv
/home/labex/project/invite.docx
사용자 홈 디렉터리 확장
os.path.expanduser()를 사용하여 ~를 사용자의 홈 디렉터리로 확장:
import os.path
# ~를 사용자의 홈 디렉터리로 확장
print(os.path.expanduser('~'))
/home/labex/project
# ~/Documents 를 전체 경로로 확장
print(os.path.expanduser('~/Documents'))
/home/labex/project/Documents
# ~가 포함된 경로에서 작동
print(os.path.expanduser('~/myfile.txt'))
/home/labex/project/myfile.txt
현재 작업 디렉터리
pathlib을 사용하여 현재 작업 디렉터리를 가져올 수 있습니다:
# Path.cwd(): 현재 작업 디렉터리 가져오기
from pathlib import Path
print(Path.cwd()) # Path 객체로 현재 작업 디렉터리 반환
/home/labex/project
새 폴더 만들기
*nix 에서 pathlib 사용:
from pathlib import Path
cwd = Path.cwd()
(cwd / 'delicious' / 'walnut' / 'waffles').mkdir()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.6/pathlib.py", line 1226, in mkdir
self._accessor.mkdir(self, mode)
File "/usr/lib/python3.6/pathlib.py", line 387, in wrapped
return strfunc(str(pathobj), *args)
FileNotFoundError: [Errno 2] No such file or directory: '/home/labex/project/delicious/walnut/waffles'
이런, 오류가 발생했습니다! ‘delicious’ 디렉터리가 존재하지 않기 때문에 그 아래에 ‘walnut’ 및 ‘waffles’ 디렉터리를 만들 수 없습니다. 이를 수정하려면 다음을 수행하십시오.
# mkdir(parents=True): 필요한 경우 디렉터리와 모든 상위 디렉터리 생성
from pathlib import Path
cwd = Path.cwd()
(cwd / 'delicious' / 'walnut' / 'waffles').mkdir(parents=True) # 중첩된 디렉터리 생성
이제 모든 것이 잘 작동합니다 :)
절대 경로 대 상대 경로
파일 경로를 지정하는 두 가지 방법이 있습니다.
- 절대 경로: 항상 루트 폴더로 시작합니다.
- 상대 경로: 프로그램의 현재 작업 디렉터리에 상대적입니다.
점 (.) 및 점 - 점 (..) 폴더도 있습니다. 이들은 실제 폴더가 아니라 경로에서 사용할 수 있는 특수 이름입니다. 폴더 이름에 대한 단일 마침표 (“점”) 는 "이 디렉터리"의 약어입니다. 두 개의 마침표 (“점 - 점”) 는 "상위 폴더"를 의미합니다.
절대 경로 처리
pathlib을 사용하여 경로가 절대 경로인지 확인하는 방법:
from pathlib import Path
Path('/').is_absolute()
True
Path('..').is_absolute()
False
로그인하여 이 퀴즈에 답하고 학습 진행 상황을 추적하세요
Path('/').is_absolute()는 무엇을 반환합니까?TrueFalseNone'/'pathlib을 사용하여 절대 경로를 추출할 수 있습니다:
from pathlib import Path
print(Path.cwd())
/home/labex/project
print(Path('..').resolve())
/home
상대 경로 처리
pathlib을 사용하여 시작 경로에서 다른 경로까지의 상대 경로를 가져올 수 있습니다:
from pathlib import Path
print(Path('/etc/passwd').relative_to('/'))
etc/passwd
경로 및 파일 유효성 검사
파일/디렉터리 존재 여부 확인
*nix 에서 pathlib 사용:
from pathlib import Path
Path('.').exists()
True
Path('setup.py').exists()
True
Path('/etc').exists()
True
Path('nonexistentfile').exists()
False
경로가 파일인지 확인
*nix 에서 pathlib 사용:
from pathlib import Path
Path('setup.py').is_file()
True
Path('/home').is_file()
False
Path('nonexistentfile').is_file()
False
로그인하여 이 퀴즈에 답하고 학습 진행 상황을 추적하세요
Path('setup.py').is_file()은 무엇을 반환합니까?'setup.py'FalseTrueNone경로가 디렉터리인지 확인
*nix 에서 pathlib 사용:
from pathlib import Path
Path('/').is_dir()
True
Path('setup.py').is_dir()
False
Path('/spam').is_dir()
False
파일 크기를 바이트 단위로 가져오기
*nix 에서 pathlib 사용:
from pathlib import Path
stat = Path('/bin/python3.6').stat()
print(stat) # stat 은 파일에 대한 다른 정보도 포함합니다
os.stat_result(st_mode=33261, st_ino=141087, st_dev=2051, st_nlink=2, st_uid=0,
--snip--
st_gid=0, st_size=10024, st_atime=1517725562, st_mtime=1515119809, st_ctime=1517261276)
print(stat.st_size) # 바이트 단위 크기
10024
디렉터리 나열
*nix 에서 pathlib을 사용하여 디렉터리 내용을 나열:
from pathlib import Path
for f in Path('/usr/bin').iterdir():
print(f)
...
/usr/bin/tiff2rgba
/usr/bin/iconv
/usr/bin/ldd
/usr/bin/cache_restore
/usr/bin/udiskie
/usr/bin/unix2dos
/usr/bin/t1reencode
/usr/bin/epstopdf
/usr/bin/idle3
...
디렉터리 파일 크기
경고
디렉터리 자체에도 크기가 있습니다! 따라서 위 섹션에서 논의된 메서드를 사용하여 경로가 파일인지 디렉터리인지 확인하는 것이 좋습니다.
*nix 에서 pathlib 사용:
from pathlib import Path
total_size = 0
for sub_path in Path('/usr/bin').iterdir():
total_size += sub_path.stat().st_size
print(total_size)
1903178911
파일 및 폴더 복사
shutil 모듈은 파일 복사뿐만 아니라 전체 폴더 복사를 위한 함수를 제공합니다.
import shutil
shutil.copy('/tmp/spam.txt', '/tmp/delicious')
/tmp/delicious/spam.txt
shutil.copy('/tmp/eggs.txt', '/tmp/delicious/eggs2.txt')
/tmp/delicious/eggs2.txt
로그인하여 이 퀴즈에 답하고 학습 진행 상황을 추적하세요
shutil.copy()Path.copy()os.copy()shutil.copytree()shutil.copy()는 단일 파일을 복사하지만 shutil.copytree()는 전체 폴더와 그 안에 포함된 모든 폴더 및 파일을 복사합니다:
import shutil
shutil.copytree('/tmp/bacon', '/tmp/bacon_backup')
/tmp/bacon_backup
이동 및 이름 바꾸기
import shutil
shutil.move('/tmp/bacon.txt', '/tmp/eggs')
/tmp/eggs/bacon.txt
대상 경로에 파일 이름을 지정할 수도 있습니다. 다음 예제에서는 소스 파일이 이동되고 이름이 바뀝니다:
shutil.move('/tmp/bacon.txt', '/tmp/eggs/new_bacon.txt')
/tmp/eggs/new_bacon.txt
eggs 폴더가 없으면 move()는 bacon.txt 의 이름을 eggs 라는 파일로 바꿉니다:
shutil.move('/tmp/bacon.txt', '/tmp/eggs')
/tmp/eggs
파일 및 폴더 삭제
Path.unlink()을 호출하면 해당 경로의 파일이 삭제됩니다.Path.rmdir()을 호출하면 해당 경로의 폴더가 삭제됩니다. 이 폴더에는 파일이나 폴더가 없어야 합니다.shutil.rmtree(path)를 호출하면 해당 경로의 폴더가 제거되고 포함된 모든 파일과 폴더도 삭제됩니다.
로그인하여 이 퀴즈에 답하고 학습 진행 상황을 추적하세요
Path.rmdir()shutil.rmtree()Path.unlink()os.remove()디렉터리 트리 순회
Path 객체에는 파일을 재귀적으로 반복하기 위한 rglob() 메서드가 있습니다.
from pathlib import Path
p = Path('/tmp/delicious')
for i in p.rglob('*'):
print(i)
/tmp/delicious/cats
/tmp/delicious/walnut
/tmp/delicious/spam.txt
/tmp/delicious/cats/catnames.txt
/tmp/delicious/cats/zophie.jpg
/tmp/delicious/walnut/waffles
/tmp/delicious/walnut/waffles/butter.txt