ฉันจะสร้างไดเรกทอรีที่ซ้อนกันได้อย่างปลอดภัยได้อย่างไร


4244

เป็นวิธีที่หรูหราที่สุดในการตรวจสอบว่าไดเรกทอรีไฟล์จะถูกเขียนไปยังอยู่และถ้าไม่สร้างไดเรกทอรีโดยใช้ 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)

มีการตั้งค่าสถานะสำหรับ "เปิด" ที่ทำให้เกิดขึ้นโดยอัตโนมัติหรือไม่


27
โดยทั่วไปคุณอาจต้องพิจารณากรณีที่ไม่มีไดเรกทอรีในชื่อไฟล์ ในเครื่อง dirname ของฉัน ('foo.txt') ให้ '' ซึ่งไม่มีอยู่และทำให้ makedirs () ล้มเหลว
Brian Hawkins

11
ใน python 2.7 os.path.mkdirไม่มีอยู่จริง os.mkdirมัน
drevicko

6
หากเส้นทางนั้นมีอยู่ไม่เพียง แต่จะตรวจสอบว่าเป็นไดเรกทอรีและไม่ใช่ไฟล์ปกติหรือวัตถุอื่น (คำตอบจำนวนมากตรวจสอบนี้) ก็จำเป็นต้องตรวจสอบว่ามันเขียนได้ (ฉันไม่พบคำตอบที่ตรวจสอบนี้)
miracle173

9
ในกรณีที่คุณมาที่นี่เพื่อสร้างไดเรกทอรีหลักของสตริงไฟล์พา ธpนี่คือข้อมูลโค้ดของฉัน:os.makedirs(p[:p.rindex(os.path.sep)], exist_ok=True)
Thamme Gowda

คำตอบ:


5185

บน 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.existsและos.makedirsสายที่จะล้มเหลวกับos.makedirs 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แต่สมมติว่ามีอีกสร้างไดเรกทอรีหลังจากการตรวจสอบครั้งแรกแล้วลบออกก่อนที่สอง - เราอาจยังคงถูกหลอก

ขึ้นอยู่กับแอปพลิเคชันอันตรายของการดำเนินการพร้อมกันอาจจะมากหรือน้อยกว่าอันตรายที่เกิดจากปัจจัยอื่น ๆ เช่นการอนุญาตไฟล์ ผู้พัฒนาจะต้องรู้เพิ่มเติมเกี่ยวกับแอปพลิเคชันเฉพาะที่กำลังพัฒนาและสภาพแวดล้อมที่คาดไว้ก่อนที่จะเลือกการนำไปใช้

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.

5
สภาวะการแย่งชิงเป็นจุดที่ดี แต่แนวทางในstackoverflow.com/questions/273192/#273208จะปกปิดความล้มเหลวในการสร้างไดเรกทอรี อย่ารู้สึกแย่กับการโหวต - คุณไม่ชอบคำตอบ มันคือสิ่งที่ให้คะแนน
แบลร์คอนราด

27
โปรดจำไว้ว่า os.path.exists () ไม่ฟรี หากกรณีปกติคือว่าไดเรกทอรีจะอยู่ที่นั่นกรณีที่ไม่ควรจัดการเป็นข้อยกเว้น กล่าวอีกนัยหนึ่งให้ลองเปิดและเขียนไฟล์ของคุณตรวจจับข้อยกเว้น OSError และทำ makedir ของคุณ () และลองอีกครั้งหรือเพิ่มอีกครั้ง สิ่งนี้จะสร้างการทำซ้ำของรหัสเว้นแต่ว่าคุณจะเขียนงานด้วยวิธีโลคอล
แอนดรู

22
os.path.existsยังส่งคืนTrueไฟล์ ฉันโพสต์คำตอบเพื่อแก้ไขปัญหานี้
คิวเมนตัส

13
ตามที่ข้อคิดเห็นของคำตอบอื่น ๆ ที่ระบุไว้ในที่นี้exists_okพารามิเตอร์ที่os.makedirs()สามารถใช้เพื่อให้ครอบคลุมการจัดการเส้นทางที่มีอยู่ก่อนหน้านี้ตั้งแต่ Python 3.2
Bobble

6
os.mkdirs()สามารถสร้างโฟลเดอร์ที่ไม่ตั้งใจได้หากตัวแยกพา ธ ออกโดยไม่ตั้งใจโฟลเดอร์ปัจจุบันไม่เป็นไปตามที่คาดไว้องค์ประกอบพา ธ ประกอบด้วยตัวคั่นพา ธ หากคุณใช้os.mkdir()ข้อบกพร่องเหล่านี้จะเพิ่มข้อยกเว้นแจ้งให้คุณทราบถึงการมีอยู่ของพวกเขา
drevicko

1241

Python 3.5+:

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

pathlib.Path.mkdirตามที่ใช้ด้านบนซ้ำสร้างไดเรกทอรีและไม่ยกข้อยกเว้นหากไดเรกทอรีที่มีอยู่แล้ว หากคุณไม่ต้องการหรือต้องการให้ผู้ปกครองสร้างขึ้นให้ข้ามไปparentsโต้แย้ง

Python 3.2+:

การใช้pathlib:

หากคุณสามารถติดตั้งปัจจุบันย้ายกลับชื่อpathlib pathlib2อย่าติดตั้งย้ายกลับ unmaintained 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 pathlib2อย่าติดตั้งย้ายกลับ unmaintained 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 จากนั้นใช้mkpathอีกครั้งเพื่อสร้างไดเรกทอรีเดียวกันใหม่mkpathเพียงแค่ใช้ข้อมูลแคชที่ไม่ถูกต้องในการสร้างไดเรกทอรีก่อนหน้านี้และจะไม่ ทำไดเรกทอรีจริงอีกครั้ง ในทางตรงกันข้ามos.makedirsไม่ต้องพึ่งพาแคชดังกล่าว ข้อ จำกัด นี้อาจใช้ได้สำหรับบางแอปพลิเคชัน


เกี่ยวกับโหมดของไดเรกทอรีโปรดอ้างอิงจากเอกสารหากคุณสนใจ


13
คำตอบนี้ครอบคลุมมากทุกกรณีพิเศษเท่าที่ฉันสามารถบอกได้ ฉันวางแผนที่จะห่อสิ่งนี้ด้วย "ถ้าไม่ใช่ os.path.isdir ()" ถึงแม้ว่าฉันคาดว่าจะมีไดเรกทอรีอยู่เกือบทุกครั้งและฉันสามารถหลีกเลี่ยงข้อยกเว้นได้
Charles L.

5
@CharlesL อาจมีข้อยกเว้นราคาถูกกว่าดิสก์ IO ของการตรวจสอบหากเหตุผลของคุณคือประสิทธิภาพ
jpmc26

1
@ jpmc26 แต่ makedirs ทำสถิติเพิ่มเติม, umask, lstat เมื่อตรวจสอบเพื่อโยน OSError เท่านั้น
kwarunek

4
นี่คือคำตอบที่ผิดเพราะมันแนะนำการแข่งขัน FS ที่มีศักยภาพ ดูคำตอบจาก Aaron Hall
sleepycal

4
ดังที่ @sleepycal ได้กล่าวว่าสิ่งนี้ได้รับผลกระทบจากสภาพการแข่งขันที่คล้ายคลึงกันกับคำตอบที่ยอมรับ หากระหว่างการเพิ่มข้อผิดพลาดและการตรวจสอบos.path.isdirคนอื่นลบโฟลเดอร์คุณจะเพิ่มข้อผิดพลาดที่ล้าสมัยและเกิดความสับสนว่ามีโฟลเดอร์อยู่
farmir

604

การใช้ลองยกเว้นและรหัสข้อผิดพลาดที่ถูกต้องจากโมดูล 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' ไว้ล่วงหน้าและลบการอนุญาตทั้งหมดออกจากนั้นคุณจะได้รับการแจ้งOSErrorเตือนerrno.EACCES(ปฏิเสธการอนุญาตข้อผิดพลาด 13)


24
คำตอบที่ยอมรับนั้นเป็นอันตรายจริง ๆ เพราะมีเงื่อนไขการแข่งขัน อย่างไรก็ตามมันง่ายกว่าดังนั้นหากคุณไม่ทราบสภาพการแข่งขันหรือคิดว่ามันจะไม่นำมาใช้กับคุณนั่นจะเป็นการเลือกครั้งแรกของคุณอย่างชัดเจน
Heikki Toivonen

15
การเพิ่มข้อยกเว้นเฉพาะเมื่อexception.errno != errno.EEXISTจะไม่สนใจกรณีและปัญหาเมื่อเส้นทางมีอยู่ แต่เป็นวัตถุที่ไม่ใช่ไดเรกทอรีเช่นไฟล์ ข้อยกเว้นควรได้รับการยกระดับอย่างสมบูรณ์หากพา ธ เป็นวัตถุที่ไม่ใช่ไดเร็กทอรี
Acumenus

178
โปรดทราบว่ารหัสข้างต้นเทียบเท่ากับos.makedirs(path,exist_ok=True)
Navin

58
@Navin exist_okพารามิเตอร์ถูกนำมาใช้ใน Python 3.2 ไม่มีอยู่ใน Python 2.x ฉันจะรวมไว้ในคำตอบของฉัน
คิวเมนตัส

26
@HeikkiToivonen พูดทางเทคนิคหากโปรแกรมอื่นกำลังแก้ไขไดเรกทอรีและไฟล์ในเวลาเดียวกันกับที่โปรแกรมของคุณโปรแกรมทั้งหมดของคุณคือสภาพการแข่งขันที่ยิ่งใหญ่ สิ่งที่จะหยุดโปรแกรมอื่นจากเพียงแค่การลบไดเรกทอรีนี้หลังจากสร้างรหัสและก่อนที่คุณจะใส่ไฟล์ลงไป
jpmc26

102

ผมเองอยากจะแนะนำให้คุณใช้ในการทดสอบแทน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()


8
หากคุณใช้ 'isdir' เท่านั้นคุณจะยังคงมีปัญหาเมื่อคุณพยายามสร้างไดเรกทอรีและไฟล์ที่มีชื่อเดียวกันนี้มีอยู่แล้วหรือไม่
MrWonderful

3
@MrWonderful ข้อยกเว้นผลลัพธ์เมื่อสร้างไดเรกทอรีเหนือไฟล์ที่มีอยู่จะสะท้อนปัญหากลับไปยังผู้โทรอย่างถูกต้อง
Damian Yerrick

79

ตรวจสอบos.makedirs: (. มันทำให้แน่ใจว่าเส้นทางที่สมบูรณ์มี) เพื่อจัดการกับความจริงที่ไดเรกทอรีอาจจะมีอยู่จับ
OSError(หากexist_okเป็นFalse(ค่าเริ่มต้น) OSErrorจะเพิ่มขึ้นหากไดเรกทอรีเป้าหมายมีอยู่แล้ว)

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

19
ด้วยการลอง / ยกเว้นคุณจะปกปิดข้อผิดพลาดในการสร้างไดเรกทอรีในกรณีที่ไม่มีไดเรกทอรีอยู่ แต่ด้วยเหตุผลบางอย่างคุณไม่สามารถทำได้
Blair Conrad

3
OSErrorจะถูกเพิ่มที่นี่หากเส้นทางเป็นไฟล์หรือไดเรกทอรีที่มีอยู่ ฉันโพสต์คำตอบเพื่อแก้ไขปัญหานี้
Acumenus

4
นี่ครึ่งทางแล้ว คุณต้องตรวจสอบเงื่อนไขข้อผิดพลาดย่อยOSErrorก่อนตัดสินใจเพิกเฉย ดูstackoverflow.com/a/5032238/763269
Chris Johnson

71

เริ่มต้นจาก 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การexist_okตั้งค่าสถานะเริ่มต้นจาก python 3.2 เช่นos.makedirs(path, exist_ok=True))


46

ข้อมูลเชิงลึกเกี่ยวกับข้อมูลเฉพาะของสถานการณ์นี้

คุณให้ไฟล์เฉพาะที่เส้นทางที่แน่นอนและคุณดึงไดเรกทอรีจากเส้นทางของไฟล์ จากนั้นตรวจสอบให้แน่ใจว่าคุณมีไดเรกทอรีแล้วคุณพยายามเปิดไฟล์เพื่ออ่าน หากต้องการแสดงความคิดเห็นในรหัสนี้:

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

dirเราต้องการที่จะหลีกเลี่ยงการเขียนทับฟังก์ชั่นในตัวที่ นอกจากนี้filepathหรืออาจfullfilepathเป็นชื่อความหมายที่ดีกว่า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)

หากไม่มีไดเรกทอรีหรือไฟล์คุณจะได้รับ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โหมด (หรือ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โทรในบล็อกแบบลองยกเว้นจะดีที่สุด

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)

34

ลองใช้os.path.existsฟังก์ชั่น

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

3
ฉันจะแสดงความคิดเห็นกับคำถาม แต่เราหมายถึง os.mkdir หรือไม่ หลามของฉัน (2.5.2) ไม่มี os.path.mkdir ....
Blair Conrad

1
ไม่มีos.path.mkdir()วิธีการ os.pathดำเนินโมดูลที่มีประโยชน์บางฟังก์ชั่นใน pathnames
Serge S.

31

ฉันได้วางลงดังต่อไปนี้ แม้ว่ามันจะไม่สามารถป้องกันความผิดพลาดได้ทั้งหมด

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

ตอนนี้ที่ฉันพูดนี้ไม่สามารถจะเข้าใจผิดจริง ๆ เพราะเรามีความเป็นไปได้ของความล้มเหลวในการสร้างไดเรกทอรีและกระบวนการอื่นสร้างมันในช่วงเวลานั้น



ปัญหาสองข้อ: (1) คุณต้องตรวจสอบเงื่อนไขข้อผิดพลาดย่อยของ OSError ก่อนตัดสินใจตรวจสอบos.path.exists- ดู stackoverflow.com/a/5032238/763269 และความสำเร็จใน (2) os.path.existsไม่ได้หมายความว่ามีไดเรกทอรีอยู่เพียงแค่เส้นทาง มีอยู่ - อาจเป็นไฟล์หรือ symlink หรือวัตถุระบบไฟล์อื่น ๆ
คริสจอห์นสัน

24

ตรวจสอบว่ามีไดเรกทอรีอยู่หรือไม่และสร้างถ้าจำเป็น?

คำตอบตรงนี้คือสมมติว่าสถานการณ์ง่าย ๆ ที่คุณไม่คาดหวังให้ผู้ใช้หรือกระบวนการอื่น ๆ ทำกับไดเรกทอรีของคุณ:

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.Pathด้วยexist_ok

มีPathวัตถุใหม่(จาก 3.4) ด้วยวิธีการมากมายที่เราต้องการใช้กับเส้นทาง - ซึ่งหนึ่งในนั้นคือmkdirหนึ่งในนั้นคือ

(สำหรับบริบทฉันกำลังติดตามตัวแทนรายสัปดาห์ของฉันด้วยสคริปต์นี่คือส่วนต่าง ๆ ของรหัสที่เกี่ยวข้องจากสคริปต์ที่ให้ฉันหลีกเลี่ยงการกดปุ่ม Stack Overflow มากกว่าหนึ่งครั้งต่อวันสำหรับข้อมูลเดียวกัน)

ก่อนนำเข้าที่เกี่ยวข้อง:

from pathlib import Path
import tempfile

เราไม่ต้องจัดการกับos.path.joinตอนนี้ - เพียงเข้าร่วมส่วนเส้นทางด้วย/:

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

จากนั้นฉันให้แน่ใจว่า idempotently ไดเรกทอรีอยู่ - 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ก่อนที่ API อื่นที่คาดว่าstrจะสามารถใช้พา ธ เหล่านั้นได้

บางที Pandas ควรได้รับการปรับปรุงเพื่อยอมรับอินสแตนซ์ของคลาสฐานนามธรรม, os.PathLike.


20

ใน 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.

@JanuszSkonieczny pypi.python.org/pypi/pathlib2เป็นย้ายกลับใหม่ คนที่มีอายุมากกว่าจะไม่มีใครดูแล
คิวเมนตัส

ตามที่ระบุไว้ในบรรทัดแรกของ readme; P แต่ backport เก่ายังคงใช้ได้สำหรับคำตอบที่นี่ และไม่มีการตั้งชื่ออาการปวดหัว ไม่จำเป็นต้องอธิบายว่าเพราะเหตุใดและเมื่อไรที่จะต้องใช้pathlibและที่ไหนpathlib2สำหรับผู้ใช้ใหม่และฉันคิดว่าข้อดีที่นี่จะคิดค่า
เสื่อมราคา

13

เอกสารหลามที่เกี่ยวข้องแสดงให้เห็นการใช้งานของรูปแบบการเข้ารหัส 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

เอกสารแนะนำนี้เพราะสภาพการแข่งขันที่กล่าวถึงในคำถามนี้ นอกจากนี้อย่างที่คนอื่นพูดถึงที่นี่มีข้อได้เปรียบด้านประสิทธิภาพในการสืบค้นเพียงครั้งเดียวแทนที่จะเป็นสองเท่าของระบบปฏิบัติการ ในที่สุดข้อโต้แย้งอาจถูกส่งต่อโดยอาจใช้โค้ดที่สองในบางกรณี - เมื่อผู้พัฒนาทราบสภาพแวดล้อมที่แอปพลิเคชันทำงานอยู่ - สามารถสนับสนุนในกรณีพิเศษที่โปรแกรมได้ตั้งค่าสภาพแวดล้อมส่วนตัวสำหรับ ตัวเอง (และอินสแตนซ์อื่น ๆ ของโปรแกรมเดียวกัน)

แม้ในกรณีนี้มันเป็นวิธีปฏิบัติที่ไม่ดีและสามารถนำไปสู่การดีบั๊กที่ไร้ประโยชน์มานาน ตัวอย่างเช่นความจริงที่เราตั้งค่าการอนุญาตสำหรับไดเรกทอรีไม่ควรปล่อยให้เรามีการกำหนดสิทธิ์การแสดงผลที่เหมาะสมสำหรับวัตถุประสงค์ของเรา ไดเรกทอรีหลักสามารถเชื่อมต่อกับสิทธิ์อื่น ๆ โดยทั่วไปโปรแกรมควรทำงานอย่างถูกต้องและโปรแกรมเมอร์ไม่ควรคาดหวังสภาพแวดล้อมที่เฉพาะเจาะจงอย่างใดอย่างหนึ่ง


11

ในPython3 , สนับสนุนการตั้งค่าos.makedirs exist_okการตั้งค่าเริ่มต้นคือFalseซึ่งหมายถึงOSErrorจะเพิ่มขึ้นหากไดเรกทอรีเป้าหมายมีอยู่แล้ว โดยการตั้งค่าexist_okเพื่อTrue, OSError(directory มี) จะถูกละเว้นและไดเรกทอรีจะไม่สามารถสร้าง

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

11

สำหรับโซลูชันแบบหนึ่งซับคุณสามารถใช้IPython.utils.path.ensure_dir_exists():

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

จากเอกสาร : ตรวจสอบว่ามีไดเรกทอรีอยู่ หากไม่มีอยู่ให้ลองสร้างมันขึ้นมาและป้องกันสภาพการแข่งขันหากกระบวนการอื่นกำลังทำแบบเดียวกัน


เอกสารใหม่ IPython อยู่ที่นี่
jkdev

3
IPythonโมดูลเป็นอย่างไม่รับประกันว่าจะให้เป็นปัจจุบัน มันมีอยู่ใน Mac ของฉัน แต่ไม่ได้ติดตั้งบน Python ของ Linux โดยทั่วไปก็ไม่ได้เป็นหนึ่งในโมดูลที่ระบุไว้ในดัชนีโมดูลหลาม
คิวเมนตัส

1
แน่ใจ ในการติดตั้งแพคเกจที่ทำงานเพียงปกติpip install ipythonหรือรวมไปถึงการพึ่งพาของคุณในrequirements.txtหรือpom.xml เอกสารประกอบ: ipython.org/install.html
tashuhka

9

คุณสามารถใช้ได้ 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


2
distutils.dir_utilไม่ได้เป็นส่วนหนึ่งของ API สาธารณะ distutil และมีปัญหาในสภาพแวดล้อมแบบมัลติเธรด: bugs.python.org/issue10948
Pod

1
ใช่. ตามที่ระบุไว้ในข้อความแรกของข้อผิดพลาดปัญหาdistutils.dir_util.mkpathคือถ้าคุณสร้างไดเรกทอรีแล้วลบออกจากภายในหรือภายนอก Python จากนั้นใช้mkpathอีกครั้งmkpathเพียงแค่ใช้ข้อมูลแคชที่ไม่ถูกต้องของการสร้างไดเรกทอรีก่อนหน้านี้และจะ ไม่ได้สร้างไดเรกทอรีอีกครั้งจริง ๆ ในทางตรงกันข้ามos.makedirsไม่ต้องพึ่งพาแคชดังกล่าว
คิวเมนตัส

8

ผมใช้os.path.exists(), ที่นี่เป็นสคริปต์ Python 3 ที่สามารถใช้เพื่อตรวจสอบว่าไดเรกทอรีที่มีอยู่สร้างหนึ่งถ้ามันไม่ได้อยู่และลบมันถ้ามันไม่อยู่ (ถ้าต้องการ)

มันแจ้งให้ผู้ใช้สำหรับการป้อนข้อมูลของไดเรกทอรีและสามารถแก้ไขได้ง่าย


6

คุณสามารถใช้os.listdirสิ่งนี้:

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

นี่ไม่ได้ตอบคำถาม
Georgy

6

ฉันพบ Q / A นี้และในตอนแรกฉันก็งงกับความล้มเหลวและข้อผิดพลาดบางอย่างที่ฉันได้รับ ฉันทำงานใน Python 3 (v.3.5 ในสภาพแวดล้อมเสมือนจริง Anaconda บนระบบ Arch Linux x86_64)

พิจารณาโครงสร้างไดเรกทอรีนี้:

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

นี่คือการทดลอง / บันทึกย่อของฉันซึ่งอธิบายสิ่งต่าง ๆ :

# ----------------------------------------------------------------------------
# [1] /programming/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


6

ฉันเห็นHeikki ToivonenและคำตอบของABBและคิดเกี่ยวกับการเปลี่ยนแปลงนี้

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


5

ทำไมไม่ใช้โมดูลกระบวนการย่อยหากทำงานบนเครื่องที่รองรับคำสั่ง mkdirด้วย-pตัวเลือก? ใช้งานได้กับ python 2.7 และ python 3.6

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

ควรทำเคล็ดลับในระบบส่วนใหญ่

ในสถานการณ์ที่การพกพาไม่สำคัญ (เช่นการใช้นักเทียบท่า) การแก้ปัญหาคือ 2 บรรทัด คุณไม่จำเป็นต้องเพิ่มตรรกะเพื่อตรวจสอบว่ามีไดเรกทอรีอยู่หรือไม่ ในที่สุดมันก็ปลอดภัยที่จะทำงานอีกครั้งโดยไม่มีผลข้างเคียงใด ๆ

หากคุณต้องการจัดการข้อผิดพลาด:

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

4

หากคุณพิจารณาสิ่งต่อไปนี้:

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

หมายถึงไดเรกทอรี (เส้นทาง) มีอยู่และเป็นไดเรกทอรี ดังนั้นสำหรับฉันวิธีนี้จะทำสิ่งที่ฉันต้องการ ดังนั้นฉันสามารถตรวจสอบว่าเป็นโฟลเดอร์ (ไม่ใช่ไฟล์) และมีอยู่


สิ่งนี้จะตอบคำถามของการสร้างไดเรกทอรีได้อย่างไร
Georgy

3

เรียกใช้ฟังก์ชัน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')

3

คุณต้องตั้งค่าเส้นทางแบบเต็มก่อนที่จะสร้างไดเรกทอรี:

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)

มันใช้งานได้สำหรับฉันและหวังว่าจะได้ผลสำหรับคุณเช่นกัน


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

รหัสของคุณที่นี่ใช้คำสั่ง (สัมผัส)

นี่จะตรวจสอบว่าไฟล์อยู่ที่นั่นหรือไม่และมันจะสร้างขึ้นมา

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.