Soru Python'da yuvalanmış bir dizini nasıl güvenli bir şekilde oluşturabilirim?


Bir dosyanın bulunacağı dizinin olup olmadığını kontrol etmenin en zarif yolu nedir ve değilse Python'u kullanarak dizini oluşturmak mı? İşte denedim:

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)

Bir şekilde, özledim os.path.exists (teşekkürler kanja, Blair ve Douglas). Şimdi sahip olduğum şey bu:

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

"Açık" için bir bayrak var mı, bu otomatik olarak gerçekleşiyor mu?


2989
2017-11-07 18:56


Menşei


Genel olarak, dosya adında dizin bulunmayan durumu dikkate almanız gerekebilir. Makinemin dirname'sinde ('foo.txt') '' var, bu da yok ve makedirlerin () başarısız olmasına neden oluyor. - Brian Hawkins
Python 2.7'de os.path.mkdir mevcut değil. Onun os.mkdir. - drevicko
yolun mevcut olması durumunda, sadece bir dizin olup olmadığını ve düzenli bir dosya ya da başka bir nesne olup olmadığını kontrol etmekle kalmaz (birçok cevap bunu kontrol eder) ayrıca yazılabilir olup olmadığını kontrol etmek de gereklidir (bunu kontrol eden bir cevap bulamadım) - miracle173
Dosya yolu dizgisinin ana dizinlerini oluşturmak için buraya geldiyseniz pİşte kod snippet'ım: os.makedirs(p[:p.rindex(os.path.sep)], exist_ok=True) - Thamme Gowda
Bu sorudaki cevapların meta tartışması - gnat


Cevaplar:


Her biri küçük bir kusur ile iyi niteliklere sahip iki cevap görüyorum, bu yüzden onu ele alacağım:

Deneyin os.path.existsve düşünün os.makedirs Yaratılış için.

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

Yorumlar ve başka yerlerde belirtildiği gibi, bir yarış durumu var - eğer dizin arasında oluşturulmuşsa os.path.exists ve os.makedirs aramalar, os.makedirs bir başarısızlıkla sonuçlanacak OSError. Ne yazık ki, battaniyeler OSError ve devam etmek, kusursuz izinler, tam disk, vb. gibi diğer etkenler nedeniyle dizinin yaratılmamasını göz ardı edeceği için, kusursuz değildir.

Bir seçenek, OSError ve gömülü hata kodunu inceleyin (bkz. Python’un OSError'undan bilgi almanın bir çapraz platform yolu var mı?):

import os, errno

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

Alternatif olarak, bir saniye olabilir os.path.existsAncak, bir başkasının, ilk kontrolden sonra dizini oluşturduğunu, sonra ikincisinden önce çıkardığını varsayalım - yine de kandırılabilirdik.

Uygulamaya bağlı olarak, eşzamanlı operasyon tehlikesi, dosya izinleri gibi diğer faktörlerin oluşturduğu tehlikeden daha az veya çok olabilir. Geliştiricinin, geliştirilmekte olan özel uygulama ve bir uygulama seçmeden önce beklenen ortamı hakkında daha fazla bilgi sahibi olması gerekecek.


3691
2017-11-07 19:06



denemek / hariç çözüm daha iyi anlaştı - Corey Goldberg
Os.path.exists () öğesinin ücretsiz olmadığını unutmayın. Normal durum, dizinin orada olacağı durumdaysa, bunun olmadığı durum istisna olarak ele alınmalıdır. Başka bir deyişle, dosyanızı açıp yazmayı deneyin, OSError istisnasını yakalayın ve errno'ya göre makedir () 'i yapın ve yeniden deneyin veya yeniden yükseltin. Bu, yazıyı yerel bir yöntemle sarmadığınız sürece kodun çoğaltılmasını sağlar. - Andrew
os.path.exists ayrıca döner True bir dosya için. Bunu ele almak için bir cevap yazdım. - A-B-B
os.mkdirs() Bir yol ayırıcısı yanlışlıkla bırakılırsa istenmeyen klasörler oluşturabilir, geçerli klasör beklendiği gibi olmaz, bir yol öğesi yol ayırıcıyı içerir. Eğer kullanırsan os.mkdir() Bu hatalar, sizi varlıklarına karşı uyararak bir istisna oluşturacaktır. - drevicko
bu yüzden gerçek cevap stackoverflow.com/questions/273192/... - user3885927


Python 3.5+:

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

pathlib.Path.mkdir Yukarıda kullanıldığı gibi, dizini yinelemeli olarak oluşturur ve dizin zaten varsa bir istisna oluşturmaz. Ebeveynlerin oluşturulmasını istemiyorsanız veya istemiyorsanız, parents argüman.

Python 3.2+:

kullanma pathlib:

Eğer yapabilirseniz, akımı yükleyin pathlib backport adlı pathlib2. Eski unmaintained backport adında yükleme yapmayın pathlib. Ardından, yukarıdaki Python 3.5+ bölümüne bakın ve bunu kullanın.

Python 3.4 kullanıyorsanız, bununla birlikte olsa bile pathlib, yararlı değil exist_ok seçeneği. Backport, daha yeni ve üstün bir uygulama sunmayı amaçlamaktadır. mkdir Bu eksik seçeneği içerir.

kullanma os:

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

os.makedirs Yukarıda kullanıldığı gibi, dizini yinelemeli olarak oluşturur ve dizin zaten varsa bir istisna oluşturmaz. Opsiyonel exist_ok argüman sadece Python 3.2+ kullanıyorsanız, varsayılan değeriyle False. Bu argüman Python 2.x'te 2.7'ye kadar mevcut değildir. Bu nedenle, Python 2.7 ile olduğu gibi manuel istisna işlemine gerek yoktur.

Python 2.7+:

kullanma pathlib:

Eğer yapabilirseniz, akımı yükleyin pathlib backport adlı pathlib2. Eski unmaintained backport adında yükleme yapmayın pathlib. Ardından, yukarıdaki Python 3.5+ bölümüne bakın ve bunu kullanın.

kullanma os:

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

Naif bir çözüm ilk önce os.path.isdir bunu takiben os.makedirsYukarıdaki çözüm iki operasyonun sırasını tersine çevirir. Bunu yaparken, dizinin oluşturulmasında çoğaltılmış bir girişimde bulunması gereken ortak bir yarış koşulunu engeller ve ayrıca dizinlerden dosyaları ayırır.

İstisnaı yakalamak ve kullanmak errno sınırlı faydası çünkü OSError: [Errno 17] File existsyani errno.EEXIST, hem dosyalar hem de dizinler için yükseltilir. Dizinin var olup olmadığını kontrol etmek daha basittir.

Alternatif:

mkpath iç içe dizini oluşturur ve dizin zaten mevcutsa hiçbir şey yapmaz. Bu hem Python 2 ve 3'te çalışır.

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

Başına Hata 10948Bu alternatifin ciddi bir sınırlaması, belirli bir yol için python işlemi başına yalnızca bir kez çalışmasıdır. Diğer bir deyişle, bir dizin oluşturmak için kullanırsanız, dizini Python'un içinden veya dışından silin, sonra kullanın mkpath tekrar aynı dizini yeniden oluşturmak mkpath sadece dizini daha önce oluşturmuş olan geçersiz önbelleğe alınmış bilgisini sessizce kullanır ve dizini yeniden oluşturmaz. Tersine, os.makedirs böyle bir önbelleğe güvenmiyor. Bu sınırlama bazı uygulamalar için uygun olabilir.


Yönetmen hakkında kip, bununla ilgilenirseniz, lütfen belgelere bakın.


815
2018-01-16 17:31



Bu cevap, söyleyebileceğim kadarıyla her özel vakayı kapsar. Dizinin neredeyse her zaman var olmasını beklediğimden ve bu şekilde istisnayı önleyebildiğim halde, "eğer os.path.isdir ()" ifadesinde bir "sarma" yazmayı planlıyorum. - Charles L.
@CharlesL. Sebebiniz performans ise, bir istisna muhtemelen çek diski IO'dan daha ucuzdur. - jpmc26
@ jpmc26 ama makedirler sadece OSError atmak için kontrol ederken, ek stat, umask, lstat yapar. - kwarunek
Bu, potansiyel bir FS yarışı şartını tanıttığı için yanlış cevaptır. Aaron Hall'un cevabına bakınız. - sleepycal
@sleepycal'ın söylediği gibi, bu kabul edilen cevap olarak benzer bir yarış durumundan muzdariptir. Hatayı yükseltip denetleme arasındaysa os.path.isdirBir başkası klasörü siler, klasörün var olduğu yanlış, eski ve kafa karıştırıcı bir hatayı kaldıracaktır. - farmir


Try deneyin ve errno modülünden doğru hata kodunu kullanarak yarış durumundan kurtulur ve platformlar arasıdır:

import os
import errno

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

Diğer bir deyişle, dizinleri oluşturmaya çalışırız, ancak eğer zaten mevcutsa, hatayı görmezden geliriz. Diğer taraftan, başka bir hata bildirilir. Örneğin, 'a' önceden oluşturup ondan tüm izinleri kaldırırsanız, bir OSError ile yükseltilmiş errno.EACCES (İzin reddedildi, hata 13).


573
2018-02-17 17:17



Kabul edilen cevap aslında tehlikelidir çünkü bir yarış koşuluna sahiptir. Yine de daha basittir, bu yüzden eğer yarış koşullarından habersizseniz ya da sizin için geçerli olmayacağını düşünüyorsanız, bu ilk tercihiniz olacaktır. - Heikki Toivonen
İstisnai yalnızca exception.errno != errno.EEXIST Yol var olduğunda kasıtsız olarak durumu göz ardı eder, ancak dosya gibi dizin dışı bir nesnedir. Yol, dizin dışı bir nesne ise ideal olarak yükseltilmelidir. - A-B-B
Yukarıdaki kodun eşdeğer olduğuna dikkat edin. os.makedirs(path,exist_ok=True) - Navin
@Navin exist_ok parametre Python 3.2'de tanıtıldı. Python 2.x'te mevcut değildir. Onu benim cevabımla birleştireceğim. - A-B-B
@HeikkiToivonen Teknik olarak, eğer başka bir program dizinleri ve dosyaları aynı anda değiştiriyorsa, programınız tüm programınız dev bir yarış durumudur. Kod oluşturduktan sonra ve gerçekten dosyaları yerleştirmeden önce bu dizini silmekten başka bir programın durması nedir? - jpmc26


Kişisel olarak kullanmanızı tavsiye ederim os.path.isdir() yerine test etmek 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

Eğer varsa:

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

Ve aptal bir kullanıcı girişi:

:: /tmp/dirname/filename.etc

... adında bir dizinle bitireceksin filename.etc bu argümana geçtiğinizde os.makedirs() eğer test ederseniz os.path.exists().


85
2018-01-14 17:57



Yalnızca 'isdir' kullanırsanız, dizini oluşturmayı denediğinizde ve aynı ada sahip bir dosya zaten var ise hala sorun yaşamayacak mısınız? - MrWonderful
@MrWonderful Varolan bir dosya üzerinde bir dizin oluştururken ortaya çıkan istisna, sorunu arayan kişiye doğru olarak yansıtır. - Damian Yerrick
Bu kötü bir tavsiye. Görmek stackoverflow.com/a/5032238/763269. - Chris Johnson


Kontrol os.makedirs: (Tam yolun mevcut olduğundan emin olur.)
 Dizinin mevcut olabileceği gerçeği ele almak için OSError'a gelin. (Eğer var_ok Yanlışsa (varsayılan) ise, hedef dizin zaten mevcutsa bir OSError kaldırılır.)

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

56
2017-11-07 19:01



try / except ile, dizin oluşturulmadığı zaman, yani dizinin bulunmadığı durumlarda hata yaparsınız, ancak bazı sebeplerden dolayı bunu yapamazsınız. - Blair Conrad
Bu tek güvenli yol. - Ali Afshar
OSError yol mevcut bir dosya veya dizin ise burada yükseltilecektir. Bunu ele almak için bir cevap yazdım. - A-B-B
Bu yarıya var. Alt hata durumunu kontrol etmeniz gerekiyor OSError görmezden gelmeye karar vermeden önce. Görmek stackoverflow.com/a/5032238/763269. - Chris Johnson


Bu durumun özellikleri ile ilgili bilgiler

Belli bir dosyayı belirli bir yolla verirsiniz ve dizini dosya yolundan alırsınız. Ardından, dizinin bulunduğundan emin olduktan sonra, okumak için bir dosya açmaya çalışın. Bu kod hakkında yorum yapmak için:

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

Yerleşik fonksiyonun üzerine yazmamaktan kaçınmak istiyoruz. dir. Ayrıca, filepath ya da belki fullfilepath muhtemelen daha iyi bir semantik adıdır filename bu yüzden daha iyi yazılır:

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

Son hedefiniz bu dosyayı açmaktır, başlangıçta yazmanız için yazıyorsunuz, ancak esasen bu hedefe (kodunuza göre) yaklaşıyorsunuz. okuma:

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

Okumak için açılışı varsayarsak

Neden orada olmasını beklediğiniz ve okuyabileceğiniz bir dosya için bir dizin hazırladınız?

Sadece dosyayı açmaya çalış.

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

Dizin veya dosya yoksa, bir IOError ilişkili bir hata numarasıyla: errno.ENOENT Platformunuza bakılmaksızın doğru hata numarasını gösterecektir. İsterseniz yakalayabilirsiniz, örneğin:

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

Yazmak için açıldığımızı varsayalım

Bu muhtemelen ne istiyorsun

Bu durumda, muhtemelen herhangi bir yarış şartıyla karşı karşıya değiliz. Öyleyse olduğun gibi yap, ama yazabilmek için yazıyla açmalısın. w modu (veya a eklemek). Ayrıca, dosyaları açmak için içerik yöneticisini kullanmak için bir Python en iyi uygulamasıdır.

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

Ancak, tüm verilerini aynı dizine koymaya çalışan birkaç Python işlemimiz olduğunu varsayalım. O zaman dizinin yaratılması konusunda bir çekişme yapabiliriz. Bu durumda en iyisi makedirs try-except bloğunda çağrı yapın.

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)

29
2018-01-22 23:49





Python 3.5'ten başlayarak, pathlib.Path.mkdir bir exist_ok bayrak:

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

Bu, yinelenen dizini oluşturur ve dizin zaten varsa bir istisna oluşturmaz.

(tıpkı os.makedirs bir var exists_ok python'dan başlayan bayrak 3.2).


28
2017-12-14 16:06