簡體   English   中英

如何安全地創建嵌套目錄?

[英]How can I safely create a nested directory?

我正在使用 Python 編寫文件。 我如何檢查:

  1. 如果父文件目錄存在
  2. 如果沒有 - 創建目錄

在 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並檢查嵌入的錯誤代碼(請參閱Is there a cross-platform way of getting information from Python's 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.

Python 3.5+:

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

上面使用的pathlib.Path.mkdir遞歸地創建目錄,如果目錄已經存在,則不會引發異常。 如果您不需要或不希望創建父母,請跳過parents參數。

Python 3.2+:

使用pathlib

如果可以,請安裝名為pathlib2的當前pathlib backport。 不要安裝名為pathlib的較舊的未維護的反向端口。 接下來,參考上面的 Python 3.5+ 部分,同樣使用它。

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

使用os

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

上面使用的os.makedirs遞歸地創建目錄,如果目錄已經存在,則不會引發異常。 僅當使用 Python 3.2+ 時,它才具有可選的exist_ok參數,默認值為False 此參數在 Python 2.x 到 2.7 中不存在。 因此,不需要像 Python 2.7 那樣手動處理異常。

Python 2.7+:

使用pathlib

如果可以,請安裝名為pathlib2的當前pathlib backport。 不要安裝名為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創建嵌套目錄,如果該目錄已經存在,則不執行任何操作。 這適用於 Python 2 和 3。

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

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


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

使用 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.EACCESOSError (權限被拒絕,錯誤 13)。

從 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 開始有一個exist_ok標志,例如os.makedirs(path, exist_ok=True)


注意:當我發布這個答案時,沒有提到其他答案exist_ok ...

我個人建議您使用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.makedirs :(它確保完整的路徑存在。)
要處理目錄可能存在的事實,請捕獲OSError (如果exist_okFalse (默認值),如果目標目錄已經存在,則會引發OSError 。)

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

試試os.path.exists函數

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

關於這種情況的具體情況的見解

您在特定路徑中提供特定文件,然后從文件路徑中提取目錄。 然后在確保您擁有該目錄之后,您嘗試打開一個文件進行讀取。 要評論此代碼:

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

我們希望避免覆蓋內置函數dir 此外, filepathfullfilepath可能是比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)

如果目錄或文件不存在,您將收到帶有相關錯誤號的IOErrorerrno.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)

我已經把以下內容。 不過,這也不是萬無一失的。

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.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 中的新功能: pathlib.Pathexist_ok

有一個新的Path對象(從 3.4 開始),其中包含許多希望與路徑一起使用的方法 - 其中之一是mkdir

(對於上下文,我正在使用腳本跟蹤我的每周代表。以下是腳本中代碼的相關部分,可以讓我避免每天針對相同的數據多次訪問 Stack Overflow。)

首先是相關的進口:

from pathlib import Path
import tempfile

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

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

然后我冪等地確保目錄存在exist_ok參數出現在 Python 3.5 中:

directory.mkdir(exist_ok=True)

這是文檔的相關部分:

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

這是腳本的更多內容 - 就我而言,我不受競爭條件的影響,我只有一個進程希望目錄(或包含的文件)在那里,並且我沒有任何嘗試刪除的內容目錄。

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))

必須先將Path對象強制轉換為str ,然后其他期望str路徑的 API 才能使用它們。

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

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

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

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

文檔中:確保目錄存在。 如果它不存在,請嘗試創建它並在另一個進程正在執行相同操作時防止出現競爭條件。

IPython 是一個擴展包,不是標准庫的一部分。

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 編碼風格(Easier to Ask for Forgiveness than Permission) 這意味着代碼

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 中執行此操作的最佳方法

#Devil
import os
directory = "./out_dir/subdir1/subdir2"
if not os.path.exists(directory):
    os.makedirs(directory)

在我對在 Python 中使用目錄時遇到的一些失敗和錯誤感到困惑之后,我發現了這個 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] https://docs.python.org/3/library/os.html#os.makedirs

您可以使用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。

最快最安全的方法是:如果不存在則創建,如果存在則跳過:

from pathlib import Path
Path("path/with/childs/.../").mkdir(parents=True, exist_ok=True)

如果在支持帶有-p選項的命令mkdir的機器上運行,為什么不使用子進程模塊? 適用於 python 2.7 和 python 3.6

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

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

在可移植性無關緊要的情況下(例如,使用 docker),解決方案是干凈的 2 行。 您也不必添加邏輯來檢查目錄是否存在。 最后,重新運行是安全的,沒有任何副作用

如果您需要錯誤處理:

from subprocess import check_call
try:
    check_call(['mkdir', '-p', 'path1/path2/path3'])
except:
    handle...

您必須在創建目錄之前設置完整路徑:

import os,sys,inspect
import pathlib

currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
your_folder = currentdir + "/" + "your_folder"

if not os.path.exists(your_folder):
   pathlib.Path(your_folder).mkdir(parents=True, exist_ok=True)

這對我有用,希望它也對你有用

如果您將文件寫入變量路徑,您可以在文件路徑上使用它來確保創​​建父目錄。

from pathlib import Path

path_to_file = Path("zero/or/more/directories/file.ext")
parent_directory_of_file = path_to_file.parent
parent_directory_of_file.mkdir(parents=True, exist_ok=True)

即使path_to_filefile.ext (零目錄深度)也可以工作。

請參閱pathlib.PurePath.parentpathlib.Path.mkdir

我看到Heikki ToivonenABB的回答並想到了這種變化。

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.exists()是一個 Python 3 腳本,可用於檢查目錄是否存在,如果不存在則創建一個,如果存在則刪除它(如果需要)。

它提示用戶輸入目錄並且可以很容易地修改。

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

 if not os.path.isdir(test_img_dir):
     os.mkdir(test_img_dir)

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

您可以為此使用os.listdir

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

如果您考慮以下情況:

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

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

這可能不能完全回答這個問題。 但我猜你的真正意圖是創建一個文件及其父目錄,因為它的內容全部在 1 個命令中。

您可以使用 pathlib 的fastcore擴展來做到這一點: path.mk_write(data)

from fastcore.utils import Path
Path('/dir/to/file.txt').mk_write('Hello World')

fastcore 文檔中查看更多信息

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

此處的代碼使用 (touch) 命令

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

您可以使用系統調用創建嵌套目錄dir1/dir2/... 說在Linux下可以這樣做:

import os
dirs='dir1/dir2/dir3'
os.system("mkdir -p {0}".format(dirs))

標志-p檢查目錄是否存在,在這種情況下不會生成任何錯誤消息。

讓我提一下,這看起來不像是完全由 Python 方法完成的,任何做同樣事情的 Python 庫都應該在內部使用上述類型的系統調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM