exception - كيف يمكنني إنشاء دليل متداخل بأمان في Python؟




path directory operating-system (21)

ما هي الطريقة الأكثر أناقة لفحص ما إذا كان الدليل سيتم كتابة ملف موجود ، وإذا لم يكن كذلك ، قم بإنشاء الدليل باستخدام بايثون؟ هذا ما جربته:

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)

هل هناك علم "مفتوح" ، ما يجعل هذا يحدث تلقائيًا؟


Answers

في Python 3.4 ، يمكنك أيضًا استخدام وحدة pathlib الجديدة 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.

إذا كنت تفكر في ما يلي:

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

يعني وجود دليل (مسار) AND هو دليل. بالنسبة لي ، هذه الطريقة تفعل ما أحتاجه. حتى أتمكن من التأكد من أنه مجلد (وليس ملفًا) موجودًا.


يمكنك استخدام mkpath

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

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

لاحظ أنه سيتم إنشاء دلائل الأسلاف كذلك.

وهي تعمل لبيثون 2 و 3.


أرى إجابتين لهما صفات جيدة ، كل منهما به عيب صغير ، لذا سأعطيه الأمر:

حاول os.path.exists ، والنظر os.makedirs للإبداع.

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

كما هو موضح في التعليقات وفي أي مكان آخر ، هناك حالة سباق - إذا تم إنشاء الدليل بين os.path.exists و os.makedirs المكالمات ، سوف تفشل OSError مع OSError . لسوء الحظ ، لا يكون نظام OSError يتم OSError بطانية واستمراريته مضمونًا ، لأنه سيتجاهل الفشل في إنشاء الدليل بسبب عوامل أخرى ، مثل الأذونات غير الكافية والقرص الكامل وما إلى ذلك.

قد يكون أحد الخيارات هو اعتراض OSError وفحص رمز الخطأ المضمن (راجع هل هناك طريقة عبر النظام الأساسي للحصول على معلومات من OSError Python ):

import os, errno

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

بدلاً من ذلك ، يمكن أن يكون هناك os.path.exists ثانيًا ، لكن لنفترض أن آخرًا أنشأ الدليل بعد التحقق الأول ، ثم os.path.exists الثاني - لا يزال من الممكن خداعنا.

اعتمادا على التطبيق ، قد يكون خطر العمليات المتزامنة أكثر أو أقل من الخطر الذي تشكله عوامل أخرى مثل أذونات الملفات. سيتعين على المطور معرفة المزيد عن التطبيق المعين الذي يجري تطويره وبيئة العمل المتوقعة قبل اختيار التنفيذ.


أود أن أوصي شخصيا أن تستخدم 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() .


للحصول على حل 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 : تأكد من وجود دليل. إذا لم يكن موجودًا ، فحاول إنشاءه وحمايته من حالة سباق إذا كانت هناك عملية أخرى تفعل نفس الشيء.


رأيت 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

في Python3 ، يدعم exist_ok وضع exist_ok . الإعداد الافتراضي هو False ، مما يعني أنه سيتم رفع OSError إذا كان الدليل الهدف موجودًا بالفعل. بواسطة exist_ok إلى True ، سيتم تجاهل OSError (الدليل موجود) ولن يتم إنشاء الدليل.

os.makedirs(path,exist_ok=True)

في Python2 ، لا يدعم exist_ok وضع 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

استخدم هذا الاختيار الأمر وإنشاء دير

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

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

حيث تستخدم شفرتك هنا أمر (اللمس)

سيتحقق هذا إذا كان الملف موجودًا إذا لم يكن موجودًا ، فسيعمل على إنشائه.


تقترح وثائق بايثون ذات الصلة استخدام أسلوب التشفير 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+:

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

pathlib.Path.mkdir كما تم استخدامه أعلاه بشكل متكرر يقوم بإنشاء الدليل ولا يقوم برفع استثناء إذا كان الدليل موجودًا بالفعل. إذا لم تكن بحاجة إلى أو تريد إنشاء الآباء ، فتخطى حجة parents .

Python 3.2+:

باستخدام pathlib :

إذا كان بإمكانك ، قم بتثبيت pathlib الحالي الخاص pathlib pathlib2 . لا تقم بتثبيت pathlib غير pathlib المدعو pathlib . بعد ذلك ، راجع قسم Python 3.5+ أعلاه واستخدمه كما هو.

إذا كنت تستخدم Python 3.4 ، على الرغم من أنه يأتي مع pathlib ، فإنه يفتقد خيار exist_ok المفيد. المقصود من backport هو تقديم تطبيق أحدث وأرقى من mkdir الذي يتضمن هذا الخيار المفقود.

باستخدام os :

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

os.makedirs كما يستخدم أعلاه بشكل متكرر ينشئ الدليل ولا يرفع استثناء إذا كان الدليل موجود بالفعل. يحتوي على الوسيطة exist_ok الاختيارية فقط في حالة استخدام Python 3.2+ ، مع قيمة افتراضية لـ False . هذه الوسيطة غير موجودة في Python 2.x حتى 2.7. على هذا النحو ، لا توجد حاجة لمعالجة الاستثناء اليدوي كما هو الحال مع Python 2.7.

Python 2.7+:

باستخدام pathlib :

إذا كان بإمكانك ، قم بتثبيت pathlib الحالي الخاص pathlib pathlib2 . لا تقم بتثبيت pathlib غير 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 الدليل المتداخلة ولا يفعل أي شيء إذا كان الدليل موجودًا بالفعل. هذا يعمل في كل من بايثون 2 و 3.

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

لكل علة 10948 ، وجود قيود شديدة على هذا البديل هو أنه يعمل مرة واحدة فقط لكل عملية بيثون لمسار معين. وبعبارة أخرى ، إذا كنت تستخدمه لإنشاء دليل ، ثم احذف الدليل من داخل أو خارج بايثون ، ثم استخدم mkpath مرة أخرى لإعادة إنشاء نفس الدليل ، mkpath ببساطة mkpath المخبأة غير الصالحة عن كونه قد أنشأ الدليل سابقًا ، لن نجعل الدليل في الواقع مرة أخرى. في المقابل ، لا تعتمد os.makedirs على أي مخبأ من هذا القبيل. قد يكون هذا القيد بخير لبعض التطبيقات.

فيما يتعلق بوضع الدليل ، يرجى الرجوع إلى الوثائق إذا كنت تهتم بها.


لقد وجدت هذا سؤال وجواب وكنت في البداية في حيرة من بعض الفشل والأخطاء التي كنت أتلقى. أعمل في Python 3 (v.3.5 في بيئة Anaconda الافتراضية على نظام Arch Linux x86_64).

خذ بعين الاعتبار بنية الدليل هذه:

└── 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
# ----------------------------------------------------------------------------

الاستنتاج: في رأيي ، "الطريقة الثانية" أكثر قوة.

[1] كيف يمكنني إنشاء دليل إذا لم يكن موجودًا؟

[2] os.makedirs


لماذا لا تستخدم وحدة المعالجة الفرعية إذا كانت تعمل على جهاز يدعم لغات shell؟ يعمل على الثعبان 2.7 والبيثون 3.6

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

يجب أن تفعل الحيلة على معظم الأنظمة.


عند العمل مع I / O ملف ، فإن الشيء المهم هو النظر

TOCTTOU (وقت الاختيار إلى وقت الاستخدام)

لذلك ، قد يؤدي إجراء عملية فحص ifثم القراءة أو الكتابة لاحقًا إلى استثناء I / O غير معالج. أفضل طريقة للقيام بذلك هي:

try:
    os.makedirs(dir_path)
except OSError as e:
    if e.errno != errno.EEXIS:
        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 الموارد ، وذلك باستخدام أدلة مؤقتة عبر 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.Path with exist_ok

هناك كائن Path جديد (اعتبارا من 3.4) مع الكثير من الأساليب التي قد يرغب المرء في استخدامها مع المسارات - واحد منها هو mkdir .

(بالنسبة للسياق ، أقوم بتتبع ممثلي الأسبوعية باستخدام برنامج نصي. إليك الأجزاء ذات الصلة من التعليمات البرمجية من النص البرمجي التي تسمح لي بتجنب ضرب أكثر من مرة واحدة في اليوم لنفس البيانات.)

أولا الواردات ذات الصلة:

from pathlib import Path
import tempfile

ليس علينا التعامل مع os.path.join الآن - فقط ضم أجزاء المسار باستخدام / :

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

بعد ذلك ، exist_ok وجود الدليل - تظهر الوسيطة exist_ok في Python 3.5:

directory.mkdir(exist_ok=True)

إليك الجزء ذو الصلة من pathlib.Path.mkdir :

إذا كانت 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 قبل APIs الأخرى التي تتوقع مسارات str يمكن استخدامها.

ربما يجب تحديث Pandas لقبول os.PathLike الطبقة الأساسية المجردة ، os.PathLike .


لقد وضعت التالي لأسفل. انها ليست مضمونة تماما رغم ذلك.

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

الآن كما أقول ، هذا ليس مضمونا حقاً ، لأننا نمتلك القدرة على الفشل في إنشاء الدليل ، وعملية أخرى تقوم بإنشائه خلال تلك الفترة.


تحقق من os.makedirs : (تأكد من وجود المسار الكامل.)
للتعامل مع حقيقة وجود الدليل ، يمكنك التقاط OSError. (إذا كانت exist_ok هي False (الافتراضي) ، يتم رفع OSError إذا كان الدليل الهدف موجودًا بالفعل.)

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

أستخدم os.path.exists() ، here نص برمجي Python 3 يمكن استخدامه للتحقق من وجود دليل ، وإنشاء واحد إذا لم يكن موجودًا ، وحذفه إذا كان موجودًا (إذا رغبت في ذلك).

فإنه يطالب المستخدمين لإدخال الدليل ويمكن تعديلها بسهولة.


بدءًا من Python 3.5 ، يحتوي exist_ok علامة 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 على علامة exists_ok تبدأ من python 3.2).


كما في:

In [3]: os.path.exists('/d/temp')
Out[3]: True

ربما إرم في os.path.isdir(...) للتأكد.





python exception path directory operating-system