python 상위 - 파이썬에서 중첩 된 디렉토리를 안전하게 만들려면 어떻게해야합니까?




경로 입력 (21)

파일이 저장 될 디렉토리가 존재하는지 확인하는 가장 우아한 방법은 무엇이며, 그렇지 않다면 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, 블레어, 더글러스)를 놓쳤다. 이것은 내가 지금 가지고있는 것입니다 :

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

"open"에 대한 플래그가 있습니까? 그러면 자동으로 실행됩니까?


Answers

쉘 언어를 지원하는 시스템에서 실행중인 경우 서브 프로세스 모듈을 사용하지 않는 이유는 무엇입니까? 파이썬 2.7 및 파이썬 3.6에서 작동합니다.

from subprocess import call
call(['mkdir', '-p', 'path1/path2/path3'])

대부분의 시스템에서 트릭을해야합니다.


나는 작은 결함이있는 좋은 자질을 가진 두 가지 대답을보고, 그래서 그것에 대한 나의 대답을 줄 것이다.

os.path.exists 시도해보고 작성을 위해 os.makedirs 를 고려하십시오.

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

주석과 다른 곳에서 언급했듯이 경쟁 조건이 있습니다 - 디렉토리가 os.path.existsos.makedirs 호출 사이에 생성되면 os.makedirsOSError 와 함께 실패합니다. 안타깝게도 OSError 포괄적으로 포착하고 계속해서 사용하는 것은 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 가있을 수 있지만 다른 하나는 첫 번째 검사 후 디렉토리를 만든 다음 두 번째 전에 제거한 것으로 가정합니다. 우리는 여전히 바보가 될 수 있습니다.

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


os.path.exists() . here 에 디렉토리가 존재하는지 확인하고, 존재하지 않으면 생성하고, 존재한다면 삭제할 수있는 Python 3 스크립트가 있습니다.

사용자에게 디렉토리 입력을 요구하며 쉽게 수정할 수 있습니다.


try except와 errno 모듈의 올바른 에러 코드를 사용하여 경쟁 조건을 제거하고 크로스 플랫폼입니다 :

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'를 미리 만들어서 모든 권한을 제거하면 errno.EACCES (Permission denied, error 13)와 함께 발생하는 OSError 발생합니다.


나는 다음과 같은 것을 내려 놓았다. 그것은 완전히 빠르지는 않습니다.

import os

dirname = 'create/me'

try:
    os.makedirs(dirname)
except OSError:
    if os.path.exists(dirname):
        # We are nearly safe
        pass
    else:
        # There was an error on creation, so make sure we know about it
        raise

이제 내가 말했듯이, 이것은 디렉토리를 만드는 데 실패 할 가능성이 있고, 그 기간에 디렉토리를 생성 할 가능성이 있기 때문에 절대적으로 안전하지 않습니다.


이 상황에 대한 통찰력

특정 경로에서 특정 파일을 지정하고 파일 경로에서 디렉토리를 가져옵니다. 그런 다음 디렉토리가 있는지 확인한 후 파일을 열어 읽으려고합니다. 이 코드에 대한 의견 :

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

우리는 내장 함수를 덮어 쓰지 않도록하고 싶다. 또한 filepath 또는 아마도 fullfilepathfilename 보다 더 나은 의미 론적 이름 일 수 있습니다.

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 모드로 열어야합니다 (또는를 추가해야합니다). 컨텍스트 관리자를 사용하여 파일을 여는 것도 파이썬의 가장 좋은 방법입니다.

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

그러나 모든 데이터를 동일한 디렉토리에 넣으려고하는 여러 파이썬 프로세스가 있다고 가정 해보십시오. 그런 다음 디렉토리 생성에 대한 논쟁이있을 수 있습니다. 이 경우 try-except 블록에서 makedirs 호출을 래핑하는 것이 가장 좋습니다.

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)

나는이 Q / A를 발견했고 나는 처음에 내가 얻었던 실패들과 오류들 중 일부에 의아해했다. Python 3 (아치 리눅스 x86_64 시스템의 Anaconda 가상 환경에서 v.3.5)에서 작업 중입니다.

다음 디렉토리 구조를 고려하십시오.

└── output/         ## dir
   ├── corpus       ## file
   ├── corpus2/     ## dir
   └── subdir/      ## dir

여기에 내 실험 / 메모가 있습니다.

# ----------------------------------------------------------------------------
# [1] https://.com/questions/273192/how-can-i-create-a-directory-if-it-does-not-exist

import pathlib

""" Notes:
        1.  Include a trailing slash at the end of the directory path
            ("Method 1," below).
        2.  If a subdirectory in your intended path matches an existing file
            with same name, you will get the following error:
            "NotADirectoryError: [Errno 20] Not a directory:" ...
"""
# Uncomment and try each of these "out_dir" paths, singly:

# ----------------------------------------------------------------------------
# METHOD 1:
# Re-running does not overwrite existing directories and files; no errors.

# out_dir = 'output/corpus3'                ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/'               ## works
# out_dir = 'output/corpus3/doc1'           ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/doc1/'          ## works
# out_dir = 'output/corpus3/doc1/doc.txt'   ## no error but no file created (os.makedirs creates dir, not files!  ;-)
# out_dir = 'output/corpus2/tfidf/'         ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/'         ## works
# out_dir = 'output/corpus3/a/b/c/d/'       ## works

# [2] https://docs.python.org/3/library/os.html#os.makedirs

# Uncomment these to run "Method 1":

#directory = os.path.dirname(out_dir)
#os.makedirs(directory, mode=0o777, exist_ok=True)

# ----------------------------------------------------------------------------
# METHOD 2:
# Re-running does not overwrite existing directories and files; no errors.

# out_dir = 'output/corpus3'                ## works
# out_dir = 'output/corpus3/'               ## works
# out_dir = 'output/corpus3/doc1'           ## works
# out_dir = 'output/corpus3/doc1/'          ## works
# out_dir = 'output/corpus3/doc1/doc.txt'   ## no error but creates a .../doc.txt./ dir
# out_dir = 'output/corpus2/tfidf/'         ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/'         ## works
# out_dir = 'output/corpus3/a/b/c/d/'       ## works

# Uncomment these to run "Method 2":

#import os, errno
#try:
#       os.makedirs(out_dir)
#except OSError as e:
#       if e.errno != errno.EEXIST:
#               raise
# ----------------------------------------------------------------------------

결론 : 제 의견으로는 "방법 2"가 더 강력합니다.

[1] 디렉토리가 없으면 어떻게 만들 수 있습니까?

[2] os.makedirs


Python 3.4에서는 새로운 pathlib 모듈을 사용할 수도 있습니다.

from pathlib import Path
path = Path("/my/directory/filename.txt")
try:
    if not path.parent.exists():
        path.parent.mkdir(parents=True)
except OSError:
    # handle error; you can also catch specific errors like
    # FileExistsError and so on.

파일 I / O로 작업 할 때 고려해야 할 중요한 사항은 다음과 같습니다.

TOCTTOU (사용 시간 확인 시간)

따라서 ifif 하여 검사를하고 나중에 읽거나 쓰는 것은 처리되지 않은 I / O 예외로 끝날 수 있습니다. 가장 좋은 방법은 다음과 같습니다.

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

os.path.exists 함수를 사용해보십시오.

if not os.path.exists(dir):
    os.mkdir(dir)

Heikki Toivonen 과 share 의 답변을 보고이 변형을 생각했습니다.

import os
import errno

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

프로그램 / 프로젝트의 진입 점에서 create_dir() 함수를 호출하십시오.

import os

def create_dir(directory):
    if not os.path.exists(directory):
        print('Creating Directory '+directory)
        os.makedirs(directory)

create_dir('Project directory')

mkpath 를 사용할 수 있습니다.

# Create a directory and any missing ancestor directories. 
# If the directory already exists, do nothing.

from distutils.dir_util import mkpath
mkpath("test")    

조상 디렉토리도 생성합니다.

Python 2와 3에서 작동합니다.


한 줄짜리 솔루션의 경우 IPython.utils.path.ensure_dir_exists() 사용할 수 있습니다.

from IPython.utils.path import ensure_dir_exists
ensure_dir_exists(dir)

documentation : 디렉토리가 존재하는지 확인하십시오. 존재하지 않는 경우 다른 프로세스가 동일한 작업을 수행하는 경우 해당 프로세스를 만들어 경쟁 조건으로부터 보호하십시오.


os.makedirs 확인하십시오 : (완전한 경로가 있는지 확인합니다.)
디렉토리가 존재한다는 사실을 처리하려면 OSError를 잡아라. (exist_ok가 False (기본값)이면 대상 디렉토리가 이미 존재하면 OSError가 발생합니다.

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

이것을 위해 os.listdir 을 사용할 수 있습니다 :

import os
if 'dirName' in os.listdir('parentFolderPath')
    print('Directory Exists')

나는 개인적으로 os.path.exists() 대신에 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

os.path.exists() 테스트하면 os.makedirs() 인수를 전달할 때 filename.etc 라는 디렉토리로 끝날 것입니다.


다음 사항을 고려한 경우 :

os.path.isdir('/tmp/dirname')

디렉토리 (경로)가 존재하고 디렉토리임을 의미합니다. 그래서 나에게 이런 식으로 내가 필요한 것을 해낸다. 따라서 폴더인지 (파일이 아닌지) 존재하는지 확인할 수 있습니다.


파이썬 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)

이 재귀 적으로 디렉터리를 만들고 디렉터리가 이미 있으면 예외를 발생시키지 않습니다.

( os.makedirs 가 파이썬 3.2부터 시작하는 exists_ok 플래그를 가지고있는 exists_ok ).


Python3 에서 os.makedirsexist_ok 설정을 지원합니다. 기본 설정은 False 입니다. 즉, 대상 디렉터리가 이미있는 경우 OSError 가 발생합니다. exist_okTrue 로 설정하면 OSError (디렉토리가 있음)가 무시되고 디렉토리가 생성되지 않습니다.

os.makedirs(path,exist_ok=True)

Python2 에서 os.makedirsexist_ok 설정을 지원하지 않습니다. 당신은 heikki-toivonen의 대답 에서 접근법을 사용할 수 있습니다 :

import os
import errno

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

11

예비 사항

  • 질문 텍스트에는 파일디렉토리 용어가 명확하게 구분되어 있지만 일부는 디렉토리가 실제로 특수 파일이라고 주장 할 수 있습니다
  • " 디렉토리의 모든 파일 "은 다음과 같은 두 가지 방식으로 해석 될 수 있습니다.
    1. 모든 직접 (또는 레벨 1) 자손
    2. 전체 디렉토리 트리에있는 모든 하위 항목 (하위 디렉토리에있는 항목 포함)
  • 질문을 받았을 때, Python 2LTS 버전이라고 상상했지만 코드 샘플은 Python 3 ( .5 )에서 실행됩니다 ( 가능한 한 Python 2 호환으로 유지할 것입니다. 내가 게시 할 파이썬 은 별도로 명시하지 않는 한 v3.5.4 버전 입니다.) 그 질문에 또 다른 키워드와 관련된 결과가 있습니다 : " 목록에 추가 ":

    • Python 2.2 이전 버전에서는 시퀀스 (iterables)가 대부분 목록 (튜플, 세트 등)으로 나타났습니다.
    • Python 2.2 에서 generator ( [Python] : Generators )의 개념 - [Python]의 호의 : yield 문 )이 소개되었습니다. 시간이 지남에 따라 생성자가 목록을 반환 / 작업 한 함수에 대해 표시되기 시작했습니다.
    • 파이썬 3 에서는 생성자가 기본 동작입니다.
    • 지금, 나는 목록을 반환하는 것이 여전히 의무적인지 (또는 생성자가 잘 할 것인지는) 모르겠지만 생성자를 list 생성자에 전달하면 list 을 생성하고 소비 할 수도있다. 아래 예제는 [Python] 의 차이점을 보여줍니다 : map ( function, iterable, ... )
    Python 2.7.10 (default, Mar  8 2016, 15:02:46) [MSC v.1600 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> m = map(lambda x: x, [1, 2, 3])  # Just a dummy lambda function
    >>> m, type(m)
    ([1, 2, 3], <type 'list'>)
    >>> len(m)
    3
    


    Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> m = map(lambda x: x, [1, 2, 3])
    >>> m, type(m)
    (<map object at 0x000001B4257342B0>, <class 'map'>)
    >>> len(m)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: object of type 'map' has no len()
    >>> lm0 = list(m)  # Construct a list out of the generator
    >>> lm0, type(lm0)
    ([1, 2, 3], <class 'list'>)
    >>>
    >>> lm1 = list(m)  # Construct a list out of the same generator
    >>> lm1, type(lm1)  # Empty list this time - generator already consumed
    ([], <class 'list'>)
    
  • 예제는 다음 구조를 가진 root_dir 이라는 디렉토리를 기반으로합니다 (이 예제는 Windows 용이지만 Ux ( Lnx ) 폴더 트리도 복제했습니다).

    E:\Work\Dev\\q003207219>tree /f "root_dir"
    Folder PATH listing for volume Work
    Volume serial number is 00000029 3655:6FED
    E:\WORK\DEV\\Q003207219\ROOT_DIR
    │   file0
    │   file1
    │
    ├───dir0
    │   ├───dir00
    │   │   │   file000
    │   │   │
    │   │   └───dir000
    │   │           file0000
    │   │
    │   ├───dir01
    │   │       file010
    │   │       file011
    │   │
    │   └───dir02
    │       └───dir020
    │           └───dir0200
    ├───dir1
    │       file10
    │       file11
    │       file12
    │
    ├───dir2
    │   │   file20
    │   │
    │   └───dir20
    │           file200
    │
    └───dir3
    


솔루션

프로그래밍 방식 :

  1. [파이썬] : os. listdir ( 경로 = '.' )

    path에 의해 주어진 디렉토리에있는 엔트리의 이름을 담고있는리스트를 반환한다. 목록은 임의의 순서로 있으며 특수 항목 '.' 포함하지 않습니다 '.' '..'


    >>> import os
    >>> root_dir = "root_dir"  # Path relative to current dir (os.getcwd())
    >>>
    >>> os.listdir(root_dir)  # List all the items in root_dir
    ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
    >>>
    >>> [item for item in os.listdir(root_dir) if os.path.isfile(os.path.join(root_dir, item))]  # Filter the items and only keep files (strip out directories)
    ['file0', 'file1']
    

    다음은 좀 더 정교한 예제입니다 ( code_os_listdir.py ).

    import os
    from pprint import pformat
    
    
    def _get_dir_content(path, include_folders, recursive):
        entries = os.listdir(path)
        for entry in entries:
            entry_with_path = os.path.join(path, entry)
            if os.path.isdir(entry_with_path):
                if include_folders:
                    yield entry_with_path
                if recursive:
                    for sub_entry in _get_dir_content(entry_with_path, include_folders, recursive):
                        yield sub_entry
            else:
                yield entry_with_path
    
    
    def get_dir_content(path, include_folders=True, recursive=True, prepend_folder_name=True):
        path_len = len(path) + len(os.path.sep)
        for item in _get_dir_content(path, include_folders, recursive):
            yield item if prepend_folder_name else item[path_len:]
    
    
    def _get_dir_content_old(path, include_folders, recursive):
        entries = os.listdir(path)
        ret = list()
        for entry in entries:
            entry_with_path = os.path.join(path, entry)
            if os.path.isdir(entry_with_path):
                if include_folders:
                    ret.append(entry_with_path)
                if recursive:
                    ret.extend(_get_dir_content_old(entry_with_path, include_folders, recursive))
            else:
                ret.append(entry_with_path)
        return ret
    
    
    def get_dir_content_old(path, include_folders=True, recursive=True, prepend_folder_name=True):
        path_len = len(path) + len(os.path.sep)
        return [item if prepend_folder_name else item[path_len:] for item in _get_dir_content_old(path, include_folders, recursive)]
    
    
    def main():
        root_dir = "root_dir"
        ret0 = get_dir_content(root_dir, include_folders=True, recursive=True, prepend_folder_name=True)
        lret0 = list(ret0)
        print(ret0, len(lret0), pformat(lret0))
        ret1 = get_dir_content_old(root_dir, include_folders=False, recursive=True, prepend_folder_name=False)
        print(len(ret1), pformat(ret1))
    
    
    if __name__ == "__main__":
        main()
    

    참고 사항 :

    • 두 가지 구현이 있습니다.
      • 하나는 발전기를 사용합니다 (물론이 예제에서는 결과를 목록으로 즉시 변환하므로 쓸모없는 것처럼 보입니다)
      • 고전적인 것 ( _old로 끝나는 함수 이름)
    • 재귀가 사용됩니다 (하위 디렉토리로 이동).
    • 각 구현에는 두 가지 기능이 있습니다.
      • 밑줄 ( _ )로 시작하는 하나 : "개인"(직접 호출해서는 안 됨) - 모든 작업을 수행합니다.
      • 공개 된 것 (이전의 래퍼) : 리턴 된 엔트리에서 초기 경로 (필요한 경우)를 제거합니다. 못생긴 구현이지만,이 시점에서 내가 올 수있는 유일한 아이디어입니다.
    • 성능 측면에서 생성자는 일반적으로 조금 더 빠르며 ( 생성반복 시간 모두 고려) 재귀 함수로 테스트하지 않았고 내부 생성기를 통해 함수 내부를 반복합니다. 우호적이다.
    • 인수를 사용하여 다른 결과 얻기


    출력 :

    (py35x64_test) E:\Work\Dev\\q003207219>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" "code_os_listdir.py"
    <generator object get_dir_content at 0x000001BDDBB3DF10> 22 ['root_dir\\dir0',
     'root_dir\\dir0\\dir00',
     'root_dir\\dir0\\dir00\\dir000',
     'root_dir\\dir0\\dir00\\dir000\\file0000',
     'root_dir\\dir0\\dir00\\file000',
     'root_dir\\dir0\\dir01',
     'root_dir\\dir0\\dir01\\file010',
     'root_dir\\dir0\\dir01\\file011',
     'root_dir\\dir0\\dir02',
     'root_dir\\dir0\\dir02\\dir020',
     'root_dir\\dir0\\dir02\\dir020\\dir0200',
     'root_dir\\dir1',
     'root_dir\\dir1\\file10',
     'root_dir\\dir1\\file11',
     'root_dir\\dir1\\file12',
     'root_dir\\dir2',
     'root_dir\\dir2\\dir20',
     'root_dir\\dir2\\dir20\\file200',
     'root_dir\\dir2\\file20',
     'root_dir\\dir3',
     'root_dir\\file0',
     'root_dir\\file1']
    11 ['dir0\\dir00\\dir000\\file0000',
     'dir0\\dir00\\file000',
     'dir0\\dir01\\file010',
     'dir0\\dir01\\file011',
     'dir1\\file10',
     'dir1\\file11',
     'dir1\\file12',
     'dir2\\dir20\\file200',
     'dir2\\file20',
     'file0',
     'file1']
    


  1. [파이썬] : os. scandir ( path = '.' ) ( !!! 파이썬 3.5 + !!! 이전 버전의 경우 파이썬 2 로 포팅 된 별도의 모듈이라고 생각하지만)

    path로 지정된 디렉토리 내의 엔트리에 대응하는 os.DirEntry 객체의 반복자를 돌려줍니다. 항목은 임의의 순서로 생성되고 특수 항목 '.''..' 는 포함되지 않습니다.

    listdir () 대신 scandir ()을 사용하면 운영 체제가 디렉토리를 검색 할 때 os.DirEntry 객체가이 정보를 노출하므로 파일 유형이나 파일 속성 정보가 필요한 코드의 성능을 크게 향상시킬 수 있습니다. 모든 os.DirEntry 메서드는 시스템 호출을 수행 할 수 있지만 is_dir()is_file() 대개 심볼릭 링크에 대한 시스템 호출 만 필요로합니다. os.DirEntry.stat() 는 Unix에서는 항상 시스템 호출이 필요하지만 Windows의 심볼릭 링크에만 필요합니다.


    >>> import os
    >>> root_dir = os.path.join(".", "root_dir")  # Explicitly prepending current directory
    >>> root_dir
    '.\\root_dir'
    >>>
    >>> scandir_iterator = os.scandir(root_dir)
    >>> scandir_iterator
    <nt.ScandirIterator object at 0x00000268CF4BC140>
    >>> [item.path for item in scandir_iterator]
    ['.\\root_dir\\dir0', '.\\root_dir\\dir1', '.\\root_dir\\dir2', '.\\root_dir\\dir3', '.\\root_dir\\file0', '.\\root_dir\\file1']
    >>>
    >>> [item.path for item in scandir_iterator]  # Will yield an empty list as it was consumed by previous iteration (automatically performed by the list comprehension)
    []
    >>>
    >>> scandir_iterator = os.scandir(root_dir)  # Reinitialize the generator
    >>> for item in scandir_iterator :
    ...     if os.path.isfile(item.path):
    ...             print(item.name)
    ...
    file0
    file1
    

    참고 사항 :

    • os.listdir 과 비슷합니다.
    • 하지만 또한 더 유연하고 (더 많은 기능을 제공합니다), 더 많은 파이썬 ic (어떤 경우에는 더 빠름)


  1. [파이썬] : os. 도보 ( top, topdown = True, onerror = 없음, followlinks = False )

    트리를 하향식 또는 상향식으로 이동하여 디렉토리 트리에서 파일 이름을 생성하십시오. 최상위 디렉토리를 루트로하는 트리의 각 디렉토리에 대해 3-tuple ( dirpath , dirnames , filenames )을 생성합니다.


    >>> import os
    >>> root_dir = os.path.join(os.getcwd(), "root_dir")  # Specify the full path
    >>> root_dir
    'E:\\Work\\Dev\\\\q003207219\\root_dir'
    >>>
    >>> walk_generator = os.walk(root_dir)
    >>> root_dir_entry = next(walk_generator)  # First entry corresponds to the root dir (that was passed as an argument)
    >>> root_dir_entry
    ('E:\\Work\\Dev\\\\q003207219\\root_dir', ['dir0', 'dir1', 'dir2', 'dir3'], ['file0', 'file1'])
    >>>
    >>> root_dir_entry[1] + root_dir_entry[2]  # Display the dirs and the files (that are direct descendants) in a single list
    ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
    >>>
    >>> [os.path.join(root_dir_entry[0], item) for item in root_dir_entry[1] + root_dir_entry[2]]  # Display all the entries in the previous list by their full path
    ['E:\\Work\\Dev\\\\q003207219\\root_dir\\dir0', 'E:\\Work\\Dev\\\\q003207219\\root_dir\\dir1', 'E:\\Work\\Dev\\\\q003207219\\root_dir\\dir2', 'E:\\Work\\Dev\\\\q003207219\\root_dir\\dir3', 'E:\\Work\\Dev\\\\q003207219\\root_dir\\file0', 'E:\\Work\\Dev\\\\q003207219\\root_dir\\file1']
    >>>
    >>> for entry in walk_generator:  # Display the rest of the elements (corresponding to every subdir)
    ...     print(entry)
    ...
    ('E:\\Work\\Dev\\\\q003207219\\root_dir\\dir0', ['dir00', 'dir01', 'dir02'], [])
    ('E:\\Work\\Dev\\\\q003207219\\root_dir\\dir0\\dir00', ['dir000'], ['file000'])
    ('E:\\Work\\Dev\\\\q003207219\\root_dir\\dir0\\dir00\\dir000', [], ['file0000'])
    ('E:\\Work\\Dev\\\\q003207219\\root_dir\\dir0\\dir01', [], ['file010', 'file011'])
    ('E:\\Work\\Dev\\\\q003207219\\root_dir\\dir0\\dir02', ['dir020'], [])
    ('E:\\Work\\Dev\\\\q003207219\\root_dir\\dir0\\dir02\\dir020', ['dir0200'], [])
    ('E:\\Work\\Dev\\\\q003207219\\root_dir\\dir0\\dir02\\dir020\\dir0200', [], [])
    ('E:\\Work\\Dev\\\\q003207219\\root_dir\\dir1', [], ['file10', 'file11', 'file12'])
    ('E:\\Work\\Dev\\\\q003207219\\root_dir\\dir2', ['dir20'], ['file20'])
    ('E:\\Work\\Dev\\\\q003207219\\root_dir\\dir2\\dir20', [], ['file200'])
    ('E:\\Work\\Dev\\\\q003207219\\root_dir\\dir3', [], [])
    

    참고 사항 :

    • 장면 아래에서 os.listdir( 사용 os.scandir가능한 경우)
    • 하위 폴더에서 되풀이하여 무거운 짐을 덜어줍니다.


  1. [파이썬] : glob. glob ( pathname, *, recursive = False ) ( [Python] : glob. iglob ( 경로 이름, *, 재귀 = False ) )

    pathname 에 일치하는 공백 일 가능성이있는 패스 명을 돌려줍니다. 패스 명은, 패스 명을 포함한 캐릭터 라인이 아니면 안됩니다. pathname 은 absolute (like /usr/src/Python-1.5/Makefile) 또는 relative (like ../../Tools/*/*.gif) 중 하나 일 수 있으며 쉘 스타일 와일드 카드를 포함 할 수 있습니다. 깨진 심볼릭 링크가 결과에 포함됩니다 (셸과 마찬가지로).
    ...
    버전 3.5에서 변경되었습니다 : "**"을사용하여 재귀 적 glob을 지원합니다.


    >>> import glob, os
    >>> wildcard_pattern = "*"
    >>> root_dir = os.path.join("root_dir", wildcard_pattern)  # Match every file/dir name
    >>> root_dir
    'root_dir\\*'
    >>>
    >>> glob_list = glob.glob(root_dir)
    >>> glob_list
    ['root_dir\\dir0', 'root_dir\\dir1', 'root_dir\\dir2', 'root_dir\\dir3', 'root_dir\\file0', 'root_dir\\file1']
    >>>
    >>> [item.replace("root_dir" + os.path.sep, "") for item in glob_list]  # Strip the dir name and the path separator from begining
    ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
    >>>
    >>> for entry in glob.iglob(root_dir + "*", recursive=True):
    ...     print(entry)
    ...
    root_dir\
    root_dir\dir0
    root_dir\dir0\dir00
    root_dir\dir0\dir00\dir000
    root_dir\dir0\dir00\dir000\file0000
    root_dir\dir0\dir00\file000
    root_dir\dir0\dir01
    root_dir\dir0\dir01\file010
    root_dir\dir0\dir01\file011
    root_dir\dir0\dir02
    root_dir\dir0\dir02\dir020
    root_dir\dir0\dir02\dir020\dir0200
    root_dir\dir1
    root_dir\dir1\file10
    root_dir\dir1\file11
    root_dir\dir1\file12
    root_dir\dir2
    root_dir\dir2\dir20
    root_dir\dir2\dir20\file200
    root_dir\dir2\file20
    root_dir\dir3
    root_dir\file0
    root_dir\file1
    

    참고 사항 :

    • 용도 os.listdir
    • 큰 나무의 경우 (특히 recursive켜져있는 경우 ) iglob선호됩니다.
    • 이름에 기반한 고급 필터링 허용 (와일드 카드로 인해)


  1. [Python] : 클래스 pathlib. 경로 ( * pathsegments ) ( !!! 파이썬 3 + !!! backported 경우 모르겠다)

    >>> import pathlib
    >>> root_dir = "root_dir"
    >>> root_dir_instance = pathlib.Path(root_dir)
    >>> root_dir_instance
    WindowsPath('root_dir')
    >>> root_dir_instance.name
    'root_dir'
    >>> root_dir_instance.is_dir()
    True
    >>>
    >>> [item.name for item in root_dir_instance.glob("*")]  # Wildcard searching for all direct descendants
    ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
    >>>
    >>> [os.path.join(item.parent.name, item.name) for item in root_dir_instance.glob("*") if not item.is_dir()]  # Display paths (including parent) for files only
    ['root_dir\\file0', 'root_dir\\file1']
    

    참고 사항 :

    • 이것이 우리의 목표를 달성하는 한 가지 방법입니다.
    • 경로를 처리 하는 것은 OOP 스타일입니다.
    • 많은 기능을 제공합니다.


  1. [파이썬] : dircache.listdir (경로) ( !!! 파이썬 3 에서 제거됨 !!! )

    • 그러나 $ {PYTHON_SRC_DIR} /Lib/dircache.py : ~ # 20 + ( v2.7.14 부터 )에 따르면, 그것은 (thin) 래퍼 일뿐입니다.os.listdir


    def listdir(path):
        """List directory contents, using cache."""
        try:
            cached_mtime, list = cache[path]
            del cache[path]
        except KeyError:
            cached_mtime, list = -1, []
        mtime = os.stat(path).st_mtime
        if mtime != cached_mtime:
            list = os.listdir(path)
            list.sort()
        cache[path] = mtime, list
        return list
    


  1. [사람] :했던 opendir (3) / [사람] : READDIR (3) / [사람] : CLOSEDIR (3) 를 통해 [파이썬] :하는 ctypes - 파이썬에 대한 외국인 함수 라이브러리 ( ! UX는 특정 !!! )

    ctypes 는 Python을위한 외국 함수 라이브러리입니다. C 호환 데이터 유형을 제공하며 DLL 또는 공유 라이브러리에서 호출 함수를 사용할 수 있습니다. 이 라이브러리를 순수 Python으로 랩핑하는 데 사용할 수 있습니다.

    code_ctypes.py :

    #!/usr/bin/env python3
    
    import sys
    from ctypes import Structure, \
        c_ulonglong, c_longlong, c_ushort, c_ubyte, c_char, c_int, \
        CDLL, POINTER, \
        create_string_buffer, get_errno, set_errno, cast, sizeof
    
    
    DT_DIR = 4
    DT_REG = 8
    
    char256 = c_char * 256
    
    class LinuxDirent64(Structure):
        _fields_ = [
            ("d_ino", c_ulonglong),
            ("d_off", c_longlong),
            ("d_reclen", c_ushort),
            ("d_type", c_ubyte),
            ("d_name", char256),
        ]
    
    LinuxDirent64Ptr = POINTER(LinuxDirent64)
    
    libc_dll = CDLL(None)
    opendir = libc_dll.opendir
    readdir = libc_dll.readdir
    closedir = libc_dll.closedir
    libc_dll.__errno_location.restype = POINTER(c_int)
    errno_loc_func = libc_dll.__errno_location
    
    
    def _get_errno():
        return "errno: {:d}({:d})".format(get_errno(), errno_loc_func().contents.value)
    
    
    def get_dir_content(path):
        ret = [path, list(), list()]
        dir_stream = opendir(create_string_buffer(path.encode()))
        if (dir_stream == 0):
            print("opendir returned NULL ({:s})".format(_get_errno()))
            return ret
        set_errno(0)
        dirent_addr = readdir(dir_stream)
        while dirent_addr:
            dirent_ptr = cast(dirent_addr, LinuxDirent64Ptr)
            dirent = dirent_ptr.contents
            name = dirent.d_name.decode()
            if dirent.d_type & DT_DIR:
                if name not in (".", ".."):
                    ret[1].append(name)
            elif dirent.d_type & DT_REG:
                ret[2].append(name)
            dirent_addr = readdir(dir_stream)
        if get_errno() or errno_loc_func().contents.value:
            print("readdir returned NULL ({:s})".format(_get_errno()))
        closedir(dir_stream)
        return ret
    
    
    def main():
        print("{:s} on {:s}\n".format(sys.version, sys.platform))
        root_dir = "root_dir"
        entries = get_dir_content(root_dir)
        print(entries)
    
    
    if __name__ == "__main__":
        main()
    

    참고 사항 :

    • 그것은에서 세 가지 기능로드 의 libc (현재 프로세스에로드를) 및 자세한 내용 확인을 위해 (그들을 호출 [스택 오버플로] : 어떻게 파일이 파이썬을 사용하여 존재하는지 여부를 확인합니까 () CristiFati의 대답 @? - 항목에서 마지막 노트 # 4 . ). 이 접근법은 Python / C edge에 매우 가깝습니다.
    • LinuxDirent64내 컴퓨터 에서 dirent.h ( 상수) 의 ctypes 표현 입니다 : Ubtu 16 x64 ( 4.10.0-40-genericlibc6-dev : amd64 ). 다른 버전 / 버전에서는 구조체 정의가 다를 수 있으며, 그렇다면 ctypes 별칭이 업데이트되어야합니다. 그렇지 않으면 정의되지 않은 동작이 발생합니다struct dirent64DT_*
    • errno_loc_func(그리고 그것과 관련된 모든 것)은 errno오류가 발생했을 때 funcs가 설정 되어 있기 때문에 그 값을 확인해야합니다. 분명히 get_errno작동하지 않습니다 (잘못된 이름으로 opendir반환 NULL하지만 get_errno여전히 0을 반환합니다). 또는 아직 이해하지 못했습니다.
    • 그것은 os.walk형식으로 데이터를 반환 합니다. 재귀 적으로 만들지는 않았지만 기존 코드부터 시작하면 상당히 사소한 작업이됩니다.
    • 모든 것은 Win 에서도 가능합니다. 데이터 (라이브러리, 함수, 구조체, 상수 등)는 다릅니다.


    출력 :

    [email protected]:~/work//q003207219$ ./code_ctypes.py
    3.5.2 (default, Nov 23 2017, 16:37:01)
    [GCC 5.4.0 20160609] on linux
    
    ['root_dir', ['dir3', 'dir2', 'dir0', 'dir1'], ['file0', 'file1']]
    


  1. [ActiveState] : win32file.FindFilesW ( !!! Win 특유의 !!! )

    Windows 유니 코드 API를 사용하여 일치하는 파일 이름의 목록을 검색합니다. API FindFirstFileW / FindNextFileW / Find close 함수에 대한 인터페이스.


    >>> import os, win32file, win32con
    >>> root_dir = "root_dir"
    >>> wildcard = "*"
    >>> root_dir_wildcard = os.path.join(root_dir, wildcard)
    >>> entry_list = win32file.FindFilesW(root_dir_wildcard)
    >>> len(entry_list)  # Don't display the whole content as it's too long
    8
    >>> [entry[-2] for entry in entry_list]  # Only display the entry names
    ['.', '..', 'dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
    >>>
    >>> [entry[-2] for entry in entry_list if entry[0] & win32con.FILE_ATTRIBUTE_DIRECTORY and entry[-2] not in (".", "..")]  # Filter entries and only display dir names (except self and parent)
    ['dir0', 'dir1', 'dir2', 'dir3']
    >>>
    >>> [os.path.join(root_dir, entry[-2]) for entry in entry_list if entry[0] & (win32con.FILE_ATTRIBUTE_NORMAL | win32con.FILE_ATTRIBUTE_ARCHIVE)]  # Only display file "full" names
    ['root_dir\\file0', 'root_dir\\file1']
    

    참고 사항 :


  1. 트릭을 수행하는 타사 패키지를 설치하십시오.
    • 대부분의 경우 위의 항목 중 하나 이상에 의존합니다 (약간의 맞춤 설정이 필요할 수 있음).


메모 (위의 내용에 대해) :

  • 코드는 이식 가능합니다 (특정 지역을 타겟팅하는 장소 - 표시된 곳 제외) 또는 교차 :
    • 플랫폼 ( Ux , Win ,)
    • 파이썬 버전 (2, 3,)
  • 위의 변형에서 여러 경로 스타일 (절대, 친척)을 사용하여 "도구"가이 방향에서 유연하다는 사실을 보여줍니다.
  • os.listdiros.scandir사용 opendir/ readdir/ closedir( [MSDN] FindFirstFile을 함수 / [MSDN] FindNextFile과 기능 / [MSDN] FindClose 함수 () "를 통해 $ {} PYTHON_SRC_DIR /Modules/posixmodule.c ")
  • win32file.FindFilesW( Win 고유의) 함수도 사용합니다 ( " $ {PYWIN32_SRC_DIR} /win32/src/win32file.i "를 통해).
  • get_dir_content(포인트 # 1에서 ) 이러한 접근 방법 중 하나를 사용하여 구현할 수 있습니다 (일부는 더 많은 작업과 약간의 작업이 필요합니다)
    • (단지 파일 대신 일부 고급 필터링 DIR)을 수행 할 수있는 다음 예를 들어 include_folders인수가 다른 (예에 의해 대체 될 수 filter_func: 인수로 경로를 취하는 함수 것) filter_func=lambda x: True(이 밖으로 제거하지 않습니다 아무것도) 내부 get_dir_content같은 : if not filter_func(entry_with_path): continue(함수가 하나 개의 항목에 대한 실패 할 경우, 그것은 건너 뜁니다),하지만 코드가되고 더 복잡한, 더 이상은 걸릴 것입니다 실행하기
  • 주의! 재귀가 사용 되었기 때문에 필자는 랩톱 ( Win 10 x64 )에서이 문제와 전혀 관련이없는 테스트를 거쳤 으며 재귀 수준이 (990 .. 1000) 범위 의 값에 도달했을 때 ( recursionlimit - 1000 (기본값)), 난 있어 :). 디렉토리 트리가 제한을 초과하면 ( FS 전문가가 아니기 때문에 가능한지 여부를 알 수 없으므로) 문제가 될 수 있습니다.
    또한 이 영역에 대한 경험이 없기 때문에 재귀 제한 을 늘리지 않으려 고합니다 ( 운영 체제 에서 스택을 늘리지 않고도 얼마를 늘릴 수 있습니까?레벨)이지만 이론적으로는 dir depth가 가능한 가장 높은 recursionlimit (해당 시스템에서) 보다 클 경우 항상 실패 할 가능성이 있습니다.
  • 코드 샘플은 설명의 목적으로 만 사용됩니다. 그게 내가 (내가 어떤 생각하지 않는 처리 계정 오류를 고려하지 않았 음을 의미합니다 try/ except/ else/ finally(: 간단하고 가능한 한 짧게 유지하는 이유는) 코드가 강력한되지 않도록, 블록). 들어 생산 , 오류 처리도 추가되어야한다

1 부 끝 1


1. Home 의 게시물 (질문 / 답변) 한도가 30000 자 ( [SE.Meta] : 귀하의 한계 파악 : 질문 제목, 게시물, 이미지 및 사용 된 링크의 최대 길이는 얼마입니까? )라는 사실 때문에, 대답은 2 부분으로 나누어 져 있습니다. [SO]를 방문하십시오 : 디렉토리의 모든 파일을 나열하려면 어떻게합니까? (@ CristiFati의 대답 - "2 부") .







python exception path directory operating-system