exception os教學 - 如何在Python中安全地創建嵌套目錄?




os用法 splitext用法 (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,Blair和Douglas)。 這就是我現在擁有的:

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

是否有“開放”的標誌,這會自動發生?


Answers

我發現這個Q / A,我最初對一些失敗和錯誤感到困惑。 我正在使用Python 3(在Arch Linux x86_64系統上的Anaconda虛擬環境中的v.3.5)。

考慮這個目錄結構:

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

這是我的實驗/筆記,它澄清了一些事情:

# ----------------------------------------------------------------------------
# [1] https://stackoverflow.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


在程序/項目的入口點調用函數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')

Python3中os.makedirs支持設置exist_ok 。 默認設置為False ,這意味著如果目標目錄已存在,將引發OSError 。 通過將exist_ok設置為True ,將忽略OSError (目錄存在)並且不會創建目錄。

os.makedirs(path,exist_ok=True)

Python2中os.makedirs不支持設置exist_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

相關的Python文檔建議使用EAFP編碼風格(更容易要求寬恕而不是權限) 。 這意味著代碼

try:
    os.makedirs(path)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise
    else:
        print "\nBE CAREFUL! Directory %s already exists." % path

比替代方案更好

if not os.path.exists(path):
    os.makedirs(path)
else:
    print "\nBE CAREFUL! Directory %s already exists." % path

文檔建議這正是因為這個問題中討論的競爭條件。 此外,正如其他人在此提到的那樣,在查詢一次而不是兩次操作系統方面存在性能優勢。 最後,在某些情況下,當開發人員知道應用程序正在運行的環境時,可能會提出有利於第二個代碼的論點 - 只有在程序為其設置私有環境的特殊情況下才能提倡這個論點。本身(以及同一程序的其他實例)。

即使在這種情況下,這也是一種不好的做法,可能會導致長時間無用的調試。 例如,我們為目錄設置權限的事實不應該讓我們為我們的目的設置適當的印象權限。 可以使用其他權限掛載父目錄。 通常,程序應該始終正常工作,程序員不應該期望一個特定的環境。


從Python 3.5開始, pathlib.Path.mkdir有一個exist_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從python 3.2開始得到一個exists_ok標誌)。


我已經放下了以下內容。 但這並非完全萬無一失。

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

現在正如我所說,這並非萬無一失,因為我們有可能無法創建目錄,而另一個進程在此期間創建它。


使用此命令檢查並創建目錄

 if not os.path.isdir(test_img_dir):
     os.mkdir(str("./"+test_img_dir))

檢查目錄是否存在並在必要時創建它?

對此的直接回答是,假設您不希望其他用戶或進程弄亂您的目錄的簡單情況:

if not os.path.exists(d):
    os.makedirs(d)

或者如果使目錄受到競爭條件的影響(即,如果檢查路徑存在後,還有其他東西可能已經成功),請執行以下操作:

import errno
try:
    os.makedirs(d)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise

但也許更好的方法是通過tempfile使用臨時目錄來迴避資源爭用問題:

import tempfile

d = tempfile.mkdtemp()

以下是在線文檔的基本要點:

mkdtemp(suffix='', prefix='tmp', dir=None)
    User-callable function to create and return a unique temporary
    directory.  The return value is the pathname of the directory.

    The directory is readable, writable, and searchable only by the
    creating user.

    Caller is responsible for deleting the directory when done with it.

Python 3.5中的新功能:帶有exist_ok

有一個新的Path對象(截至3.4),有許多方法需要與路徑一起使用 - 其中一個是mkdir

(對於上下文,我使用腳本跟踪我的每周代表。這是腳本中代碼的相關部分,這些代碼允許我避免每天多次針對相同數據訪問。)

首先是相關進口:

from pathlib import Path
import tempfile

我們現在不必處理os.path.join - 只需使用/連接路徑部分:

directory = Path(tempfile.gettempdir()) / 'sodata'

然後我exist_ok確保目錄存在 - 在Python 3.5中顯示exist_ok參數:

directory.mkdir(exist_ok=True)

這是pathlib.Path.mkdir的相關部分:

如果exist_ok為true, FileExistsError忽略FileExistsError異常(與POSIX mkdir -p命令相同的行為),但exist_ok是最後一個路徑組件不是現有的非目錄文件。

這裡有一些腳本 - 在我的情況下,我不受競爭條件的限制,我只有一個進程需要目錄(或包含文件),並且我沒有任何東西試圖刪除目錄。

todays_file = directory / str(datetime.datetime.utcnow().date())
if todays_file.exists():
    logger.info("todays_file exists: " + str(todays_file))
    df = pd.read_json(str(todays_file))

在期望str路徑可以使用它們的其他API之前,必須將Path對象強制轉換為str

也許應該更新Pandas以接受抽象基類os.PathLike


使用文件I / O時,需要考慮的重要事項是

TOCTTOU(檢查時間到使用時間)

因此,使用if進行檢查然後再讀取或寫入可能會導致未處理的I / O異常。 最好的方法是:

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

Python 3.5+:

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

pathlib.Path.mkdir所使用的pathlib.Path.mkdir以遞歸方式創建目錄,並且如果該目錄已存在則不會引發異常。 如果您不需要或不想創建parents ,請跳過parents參數。

Python 3.2+:

使用pathlib

如果可以,請安裝名為pathlib2的當前pathlib backport。 不要安裝名為pathlib的舊的非維護pathlib 。 接下來,請參閱上面的Python 3.5+部分並使用它。

如果使用Python 3.4,即使它帶有pathlib ,它也缺少有用的exist_ok選項。 backport旨在提供更新更好的mkdir實現,其中包括這個缺少的選項。

使用os

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

os.makedirs使用的os.makedirs以遞歸方式創建目錄,如果該目錄已存在則不會引發異常。 它只有在使用Python 3.2+時才有可選的exist_ok參數,默認值為False 。 這個參數在Python 2.x中不存在,最高可達2.7。 因此,不需要像Python 2.7那樣進行手動異常處理。

Python 2.7+:

使用pathlib

如果可以,請安裝名為pathlib2的當前pathlib backport。 不要安裝名為pathlib的舊的非維護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 exists ,即errno.EEXIST ,為文件和目錄引發。 僅檢查目錄是否存在更可靠。

替代方案:

mkpath創建嵌套目錄,如果該目錄已存在mkpath執行任何操作。 這適用於Python 2和3。

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

根據Bug 10948 ,這種替代方案的一個嚴重限制是,對於給定路徑,每個python進程只能運行一次。 換句話說,如果你用它來創建一個目錄,然後從Python內部或外部刪除目錄,然後再次使用mkpath重新創建相同的目錄, mkpath將默默地使用其先前創建目錄的無效緩存信息,並且實際上不會再次創建目錄。 相比之下, os.makedirs並不依賴於任何此類緩存。 對於某些應用,此限制可能沒問題。

關於目錄的模式 ,如果您關心它,請參閱文檔。


您可以使用os.listdir

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

在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.

如果在支持shell語言的機器上運行,為什麼不使用子進程模塊? 適用於python 2.7和python 3.6

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

應該在大多數係統上做到這一點。


對於IPython.utils.path.ensure_dir_exists()解決方案,您可以使用IPython.utils.path.ensure_dir_exists()

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

documentation確保存在目錄。 如果它不存在,嘗試創建它並防止競爭條件,如果另一個進程正在做同樣的事情。


import os
if os.path.isfile(filename):
    print "file exists"
else:
    "Your code here"

這裡的代碼使用(touch)命令

這將檢查文件是否存在,如果不存在,那麼它將創建它。


我看到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

我個人建議您使用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

...如果使用os.path.exists()測試,當您將該參數傳遞給os.makedirs()時,您最終會得到一個名為filename.etc的目錄。


如果您考慮以下因素:

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

表示存在目錄(路徑)並且是目錄。 所以對我來說這樣做是我需要的。 所以我可以確保它是文件夾(不是文件)並且存在。


檢查os.makedirs :(確保存在完整路徑。)
要處理目錄可能存在的事實,請捕獲OSError。 (如果exist_ok為False(默認值),則在目標目錄已存在的情況下引發OSError。)

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

使用try除了和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

換句話說,我們嘗試創建目錄,但如果它們已經存在,我們會忽略錯誤。 另一方面,報告任何其他錯誤。 例如,如果您事先創建errno.EACCES '並從中刪除所有權限,則會出現使用errno.EACCES引發的OSError (權限被拒絕,錯誤13)。


from time import time

t = time()
  • t - 浮點數,適用於時間間隔測量。

Unix和Windows平台有一些區別。





python exception path directory operating-system