[python] 중첩 디렉토리를 안전하게 만드는 방법은 무엇입니까?

파일을 작성할 디렉토리가 존재하는지 여부를 확인하고 그렇지 않은 경우 Python을 사용하여 디렉토리를 작성하는 가장 우아한 방법은 무엇입니까? 내가 시도한 것은 다음과 같습니다.

import os

file_path = "/my/directory/filename.txt"
directory = os.path.dirname(file_path)

try:
    os.stat(directory)
except:
    os.mkdir(directory)       

f = file(filename)

어떻게 든, 나는 놓쳤다 os.path.exists(kanja, Blair와 Douglas에게 감사한다). 이것이 내가 지금 가진 것입니다.

def ensure_dir(file_path):
    directory = os.path.dirname(file_path)
    if not os.path.exists(directory):
        os.makedirs(directory)

“open”에 대한 플래그가 있습니까? 자동으로 발생합니까?



답변

Python ≥ 3.5에서는 다음을 사용하십시오 pathlib.Path.mkdir.

from pathlib import Path
Path("/my/directory").mkdir(parents=True, exist_ok=True)

이전 버전의 Python의 경우 작은 결함이있는 좋은 품질의 두 가지 답변을 보았습니다.

시도 하고 창조를 os.path.exists고려 하십시오 os.makedirs.

import os
if not os.path.exists(directory):
    os.makedirs(directory)

의견과 다른 곳에서 언급했듯이 경쟁 조건이 있습니다. 디렉토리가 os.path.existsos.makedirs호출 사이에 생성 os.makedirs되면는로 실패합니다 OSError. 불행히도, OSError불완전한 권한, 전체 디스크 등과 같은 다른 요인으로 인해 디렉토리를 만들지 못하는 것을 무시할 수 있기 때문에 담요 잡기 및 계속은 절대 쉽지 않습니다.

하나의 옵션은 OSError포함 된 오류 코드 를 포착 하고 검사하는 것입니다 (Python의 OSError에서 정보를 가져 오는 플랫폼 간 방법이 있습니까)를 참조하십시오 .

import os, errno

try:
    os.makedirs(directory)
except OSError as e:
    if e.errno != errno.EEXIST:
        raise

또는 두 번째가있을 수 os.path.exists있지만 다른 첫 번째 검사 후에 디렉토리를 만든 다음 두 번째 검사 전에 디렉토리를 제거했다고 가정합니다. 여전히 속일 수 있습니다.

응용 프로그램에 따라 동시 작업의 위험은 파일 권한과 같은 다른 요인으로 인한 위험보다 많거나 적을 수 있습니다. 개발자는 구현을 선택하기 전에 개발중인 특정 응용 프로그램과 예상 환경에 대해 더 많이 알아야합니다.

최신 버전의 Python FileExistsError은 (3.3 이상에서) 노출 함으로써이 코드를 상당히 개선합니다 …

try:
    os.makedirs("path/to/directory")
except FileExistsError:
    # directory already exists
    pass

… 그리고 키워드 인수가 os.makedirs호출exist_ok 되도록 허용 합니다 (3.2 이상).

os.makedirs("path/to/directory", exist_ok=True)  # succeeds even if directory exists.


답변

파이썬 3.5 이상 :

import pathlib
pathlib.Path('/my/directory').mkdir(parents=True, exist_ok=True) 

pathlib.Path.mkdir위에서 사용한대로 디렉토리를 재귀 적으로 작성하고 디렉토리가 이미 존재하는 경우 예외를 발생시키지 않습니다. 부모를 만들거나 필요로하지 않으려면 parents인수를 건너 뜁니다 .

파이썬 3.2 이상 :

사용 pathlib:

가능하면 pathlib이라는 현재 백 포트를 설치하십시오 pathlib2. 이름이없는 유지 관리되지 않은 이전 백 포트를 설치하지 마십시오 pathlib. 다음으로 위의 Python 3.5+ 섹션을 참조하여 동일하게 사용하십시오.

Python 3.4를 사용하는 경우와 함께 제공되지만 pathlib유용한 exist_ok옵션 이 누락되었습니다 . 백 포트는 mkdir이 누락 된 옵션을 포함 하는 새롭고 우수한 구현을 제공하기위한 것 입니다.

사용 os:

import os
os.makedirs(path, exist_ok=True)

os.makedirs위에서 사용한대로 디렉토리를 재귀 적으로 작성하고 디렉토리가 이미 존재하는 경우 예외를 발생시키지 않습니다. exist_okPython 3.2 이상을 사용하는 경우에만 선택적 인수가 있으며 기본값은 False입니다. 이 인수는 Python 2.x에서 2.7까지 존재하지 않습니다. 따라서 Python 2.7에서와 같이 수동 예외 처리가 필요하지 않습니다.

파이썬 2.7+ :

사용 pathlib:

가능하면 pathlib이라는 현재 백 포트를 설치하십시오 pathlib2. 이름이없는 유지 관리되지 않은 이전 백 포트를 설치하지 마십시오 pathlib. 다음으로 위의 Python 3.5+ 섹션을 참조하여 동일하게 사용하십시오.

사용 os:

import os
try:
    os.makedirs(path)
except OSError:
    if not os.path.isdir(path):
        raise

나이브 용액을 먼저 사용할 수도 있지만 os.path.isdir다음 os.makedirs반전 두 동작들의 순서 상기 용액. 이렇게하면 디렉토리를 만들려는 중복 된 시도와 관련된 일반적인 경쟁 조건이 방지되고 디렉토리에서 파일이 명확 해집니다.

예를 들어 , 파일과 디렉토리 모두에 대해 예외가 발생하여 사용하는 errno것은 유용성이 제한적 입니다. 디렉토리가 존재하는지 확인하는 것이 더 안정적입니다.OSError: [Errno 17] File existserrno.EEXIST

대안 :

mkpath중첩 된 디렉토리를 작성하고 디렉토리가 이미 존재하면 아무 것도 수행하지 않습니다. 이것은 Python 2와 3에서 모두 작동합니다.

import distutils.dir_util
distutils.dir_util.mkpath(path)

버그 10948 당 ,이 대안의 심각한 제한은 주어진 경로에 대해 파이썬 프로세스 당 한 번만 작동한다는 것입니다. 즉, 디렉토리를 생성하기 위해 디렉토리를 사용하는 경우 Python 내부 또는 외부에서 디렉토리를 삭제 한 다음 mkpath다시 사용 하여 동일한 디렉토리를 다시 mkpath생성하고 이전에 디렉토리를 생성 한 잘못된 캐시 된 정보를 자동으로 사용합니다. 실제로 디렉토리를 다시 만드십시오. 대조적으로, os.makedirs그러한 캐시에 의존하지 않습니다. 일부 응용 프로그램에서는이 제한이 적용됩니다.


디렉토리 모드 와 관련하여 관심이있는 경우 문서를 참조하십시오.


답변

errno 모듈의 try except 및 올바른 오류 코드를 사용하면 경쟁 조건이 제거되고 크로스 플랫폼이 있습니다.

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

다시 말해 디렉토리를 만들려고하지만 이미 존재하는 경우 오류를 무시합니다. 반면에 다른 오류는보고됩니다. 예를 들어, 사전에 dir ‘a’를 작성하고 모든 권한을 제거하면 (권한 거부, 오류 13) OSError과 함께 발생 errno.EACCES합니다.


답변

개인적으로 os.path.isdir()대신 테스트에 사용 하는 것이 좋습니다 os.path.exists().

>>> os.path.exists('/tmp/dirname')
True
>>> os.path.exists('/tmp/dirname/filename.etc')
True
>>> os.path.isdir('/tmp/dirname/filename.etc')
False
>>> os.path.isdir('/tmp/fakedirname')
False

당신이 가지고 있다면:

>>> dir = raw_input(":: ")

그리고 어리석은 사용자 입력 :

:: /tmp/dirname/filename.etc

…로 테스트하면 filename.etc인수를 전달할 때 이름이 지정된 디렉토리로 끝납니다 .os.makedirs()os.path.exists()


답변

점검 os.makedirs: (전체 경로가 존재하는지 확인하십시오.)
디렉토리가 존재할 수있는 사실을 처리하려면 catch하십시오 OSError. ( exist_okis False(기본값) 인 OSError경우 대상 디렉토리가 이미 존재하면 ) 가 발생합니다.

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass


답변

파이썬 3.5부터 pathlib.Path.mkdirexist_ok플래그를 :

from pathlib import Path
path = Path('/my/directory/filename.txt')
path.parent.mkdir(parents=True, exist_ok=True)
# path.parent ~ os.path.dirname(path)

이는 재귀 적으로 디렉토리를 작성하며 디렉토리가 이미 존재하는 경우 예외를 발생시키지 않습니다.

( 파이썬 3.2에서 시작 os.makedirs하는 exist_ok플래그 가있는 것처럼 os.makedirs(path, exist_ok=True))


답변

이 상황의 특성에 대한 통찰력

특정 경로에 특정 파일을 제공하고 파일 경로에서 디렉토리를 가져옵니다. 그런 다음 디렉토리가 있는지 확인한 후 읽을 파일을 열려고 시도하십시오. 이 코드에 주석을 달려면

filename = "/my/directory/filename.txt"
dir = os.path.dirname(filename)

내장 함수 덮어 쓰기를 피하고 싶습니다 dir. 또한, filepath아마도 아마도 아마도 fullfilepath더 나은 의미 론적 이름 filename이므로 아마도 더 잘 쓰여질 것입니다 :

import os
filepath = '/my/directory/filename.txt'
directory = os.path.dirname(filepath)

최종 목표는 쓰기를 들어, 처음에이 파일을 열 수 상태,하지만 당신은 본질적으로의 파일을 열어이 같은 (코드 기준)이 목표에 접근하고 읽기 :

if not os.path.exists(directory):
    os.makedirs(directory)
f = file(filename)

독서를위한 오프닝 가정

거기에 있고 읽을 수있는 파일의 디렉토리를 만드는 이유는 무엇입니까?

파일을 열어보십시오.

with open(filepath) as my_file:
    do_stuff(my_file)

디렉토리 또는 파일이 없으면 IOError관련 오류 번호 errno.ENOENT가 나타납니다. 플랫폼에 관계없이 올바른 오류 번호를 가리 킵니다. 원하는 경우 다음과 같이 잡을 수 있습니다.

import errno
try:
    with open(filepath) as my_file:
        do_stuff(my_file)
except IOError as error:
    if error.errno == errno.ENOENT:
        print 'ignoring error because directory or file is not there'
    else:
        raise

우리가 글쓰기를 시작한다고 가정

이다 아마 당신이 원하는 것입니다.

이 경우 경쟁 조건이 없을 수 있습니다. 따라서 그대로 작업을 수행하지만 쓰기를 위해서는 w모드 로 열어야 합니다 (또는 a추가해야 함). 컨텍스트 관리자를 사용하여 파일을 여는 것도 Python 모범 사례입니다.

import os
if not os.path.exists(directory):
    os.makedirs(directory)
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

그러나 모든 데이터를 동일한 디렉토리에 넣으려고 시도하는 여러 Python 프로세스가 있다고 가정하십시오. 그런 다음 디렉토리 작성에 대한 경합이있을 수 있습니다. 이 경우에는 makedirs호출을 try-except 블록 으로 감싸는 것이 가장 좋습니다 .

import os
import errno
if not os.path.exists(directory):
    try:
        os.makedirs(directory)
    except OSError as error:
        if error.errno != errno.EEXIST:
            raise
with open(filepath, 'w') as my_file:
    do_stuff(my_file)