การพิจารณาว่าไดเร็กทอรีสามารถเขียนได้หรือไม่


101

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

คำตอบ:


185

แม้ว่าสิ่งที่ Christophe แนะนำจะเป็นโซลูชัน Pythonic มากกว่า แต่โมดูลระบบปฏิบัติการจะมีฟังก์ชัน os.accessเพื่อตรวจสอบการเข้าถึง:

os.access('/path/to/folder', os.W_OK) # W_OK ใช้สำหรับเขียน R_OK สำหรับการอ่าน ฯลฯ


4
"ขอให้อภัยได้ง่ายขึ้น" ไม่ใช่วิธีที่ดีที่สุดแม้ใน Python จะขึ้นอยู่กับสถานการณ์ ในบางครั้งขอแนะนำให้ "ขออนุญาต" เช่นเดียวกับวิธีการ os.access () ที่กล่าวถึงเช่นเมื่อความน่าจะเป็นที่จะต้องตรวจจับข้อผิดพลาดสูง
mjv

53
การทดสอบไดเร็กทอรีสำหรับบิตการเขียนนั้นไม่เพียงพอหากคุณต้องการเขียนไฟล์ลงในไดเร็กทอรี คุณจะต้องทดสอบบิตดำเนินการเช่นกันหากคุณต้องการเขียนลงในไดเร็กทอรี os.access ('/ path / to / folder', os.W_OK | os.X_OK) ด้วย os.W_OK ด้วยตัวเองคุณสามารถลบไดเร็กทอรีเท่านั้น (และเฉพาะในกรณีที่ไดเร็กทอรีว่าง)
fthinker

4
gotcha ของผู้อื่นos.access()คือการตรวจสอบโดยใช้จริง UID และ GID ไม่ได้มีประสิทธิภาพคน สิ่งนี้อาจทำให้เกิดความแปลกในสภาพแวดล้อม SUID / SGID ('แต่สคริปต์เรียกใช้ setuid root ทำไมจึงไม่สามารถเขียนลงในไฟล์ได้')
Alexios

5
บางทีโปรแกรมก็แค่อยากรู้โดยไม่จำเป็นต้องเขียนจริงๆ มันอาจต้องการเปลี่ยนรูปลักษณ์และ / หรือลักษณะการทำงานของ GUI ตามคุณสมบัติ ในกรณีนั้นฉันจะไม่คิดว่า pythonic จะเขียนและลบไฟล์เหมือนกับการทดสอบ
Bachsau

1
เพิ่งทดสอบกับการแชร์เครือข่าย Windows os.access(dirpath, os.W_OK | os.X_OK)ส่งคืน True แม้ว่าฉันจะไม่มีสิทธิ์เขียน
iamanigeeit

69

อาจดูแปลกที่จะแนะนำสิ่งนี้ แต่สำนวน Python ทั่วไปคือ

การขอการอภัยนั้นง่ายกว่าการขออนุญาต

ตามสำนวนนั้นอาจมีคนพูดว่า:

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


6
+1 Python หรือไม่นี่เป็นวิธีที่เชื่อถือได้มากที่สุดในการทดสอบการเข้าถึง
John Knoeller

5
นอกจากนี้ยังดูแลข้อผิดพลาดอื่น ๆ ที่อาจเกิดขึ้นเมื่อเขียนลงดิสก์เช่นไม่มีพื้นที่ว่างเหลืออยู่ นั่นคือพลังแห่งความพยายาม .. คุณไม่จำเป็นต้องจำทุกอย่างที่ผิดพลาด ;-)
Jochen Ritzel

4
ขอบคุณเพื่อน. ตัดสินใจที่จะใช้ os.access เนื่องจากความเร็วเป็นปัจจัยสำคัญในสิ่งที่ฉันทำที่นี่แม้ว่าฉันจะเข้าใจข้อดีของ "การขอการให้อภัยง่ายกว่าการขออนุญาต" ก็ตาม ;)
illuminatedtiger

4
เป็น IDIO ที่ยอดเยี่ยม ... m - โดยเฉพาะอย่างยิ่งเมื่อใช้ร่วมกับสำนวนอื่นด้วยexcept: passวิธีนี้คุณจะสามารถมองโลกในแง่ดีและคิดว่าตัวเองสูง / ปิดการเสียดสี ตอนนี้ทำไมฉันถึงต้องการเช่นพยายามเขียนบางสิ่งลงในทุกไดเร็กทอรีในระบบไฟล์ของฉันเพื่อสร้างรายการตำแหน่งที่เขียนได้
Tomasz Gandor

4
บางทีโปรแกรมก็แค่อยากรู้โดยไม่จำเป็นต้องเขียนจริงๆ มันอาจต้องการเปลี่ยนรูปลักษณ์และ / หรือลักษณะการทำงานของ GUI ตามคุณสมบัติ ในกรณีนั้นฉันจะไม่คิดว่า pythonic จะเขียนและลบไฟล์เหมือนกับการทดสอบ
Bachsau

19

วิธีแก้ปัญหาของฉันโดยใช้tempfileโมดูล:

import tempfile
import errno

def isWritable(path):
    try:
        testfile = tempfile.TemporaryFile(dir = path)
        testfile.close()
    except OSError as e:
        if e.errno == errno.EACCES:  # 13
            return False
        e.filename = path
        raise
    return True

ปรับปรุง: หลังจากการทดสอบรหัสอีกครั้งบน Windows ผมเห็นว่ามีแน่นอนปัญหาเมื่อใช้ tempfile มีให้ดูissue22107: แปลความหมายผิดโมดูล tempfile เข้าถึงปฏิเสธข้อผิดพลาดใน Windows ในกรณีของไดเร็กทอรีที่ไม่สามารถเขียนได้โค้ดจะแฮงค์เป็นเวลาหลายวินาทีและในที่สุดก็พ่นIOError: [Errno 17] No usable temporary file name foundไฟล์. บางทีนี่อาจเป็นสิ่งที่ผู้ใช้ 21171842 สังเกตเห็น? น่าเสียดายที่ปัญหานี้ไม่ได้รับการแก้ไขในตอนนี้เพื่อจัดการกับปัญหานี้จำเป็นต้องตรวจจับข้อผิดพลาดด้วย:

    except (OSError, IOError) as e:
        if e.errno == errno.EACCES or e.errno == errno.EEXIST:  # 13, 17

แน่นอนว่าความล่าช้ายังคงปรากฏอยู่ในกรณีเหล่านี้


1
ฉันคิดว่าอันที่ใช้ tempfile นั้นสะอาดกว่าเพราะแน่นอนว่ามันไม่ทิ้งสิ่งตกค้าง
ตั๊กแตน

3
tempfileวิธีการนี้จะไม่ทำงานโดยใช้ ใช้งานได้ก็ต่อเมื่อไม่มีOSErrorความหมายที่ได้รับอนุญาตให้เขียน / ลบ มิฉะนั้นจะไม่ใช่return Falseเพราะไม่มีการส่งคืนข้อผิดพลาดและสคริปต์จะไม่ดำเนินการต่อหรือออก ไม่มีอะไรจะกลับมา มันติดอยู่ที่เส้นนั้น อย่างไรก็ตามการสร้างไฟล์ที่ไม่ใช่ชั่วคราวเช่นคำตอบของ khattam จะทำงานได้ทั้งเมื่ออนุญาตหรือปฏิเสธ ช่วยด้วย?

10

สะดุดในหัวข้อนี้กำลังค้นหาตัวอย่างสำหรับใครบางคน ผลลัพธ์แรกใน Google ขอแสดงความยินดี!

มีคนพูดถึงวิธี Pythonic ในการทำในเธรดนี้ แต่ไม่มีตัวอย่างโค้ดง่ายๆ? จัดให้เลยสำหรับใครก็ตามที่สะดุดใน:

import sys

filepath = 'C:\\path\\to\\your\\file.txt'

try:
    filehandle = open( filepath, 'w' )
except IOError:
    sys.exit( 'Unable to write to file ' + filepath )

filehandle.write("I am writing this text to the file\n")

การดำเนินการนี้จะพยายามเปิด filehandle สำหรับการเขียนและออกโดยมีข้อผิดพลาดหากไม่สามารถเขียนไฟล์ที่ระบุได้: สิ่งนี้อ่านง่ายกว่ามากและเป็นวิธีที่ดีกว่าในการดำเนินการมากกว่าการทำ precheck บนเส้นทางไฟล์หรือไดเร็กทอรี เนื่องจากหลีกเลี่ยงสภาพการแข่งขัน กรณีที่ไฟล์ไม่สามารถเขียนได้ระหว่างเวลาที่คุณเรียกใช้ precheck และเมื่อคุณพยายามเขียนลงไฟล์จริงๆ


1
สิ่งนี้ใช้กับไฟล์ไม่ใช่ไดเร็กทอรีซึ่งเป็นสิ่งที่ OP ถาม คุณสามารถมีไฟล์ในไดเร็กทอรีและมีไดเร็กทอรีที่ไม่สามารถเขียนได้ แต่มีไฟล์อยู่หากมีไฟล์อยู่แล้ว สิ่งนี้อาจมีความสำคัญในการดูแลระบบซึ่งคุณเช่นสร้างไฟล์บันทึกที่คุณต้องการมีอยู่แล้ว แต่ไม่ต้องการให้คนใช้ไดเรกทอรีบันทึกสำหรับพื้นที่ชั่วคราว
Mike S

... และที่จริงฉันก็โหวตลงไปซึ่งตอนนี้ฉันคิดว่ามันผิดพลาด มีประเด็นดังที่ Rohaq กล่าวถึงพร้อมเงื่อนไขการแข่งขัน มีปัญหาอื่น ๆ บนแพลตฟอร์มต่างๆที่คุณสามารถทดสอบไดเร็กทอรีและดูเหมือนว่าเขียนได้ แต่จริงๆแล้วมันไม่ได้เป็นเช่นนั้น การตรวจสอบการเขียนไดเร็กทอรีข้ามแพลตฟอร์มนั้นยากกว่าที่คิด ตราบเท่าที่คุณทราบถึงปัญหานี้อาจเป็นเทคนิคที่ดี ฉันมองจากมุมมองของ UNIX-y เกินไปซึ่งเป็นความผิดพลาดของฉัน มีคนแก้ไขคำตอบนี้เพื่อให้ฉันลบ -1 ได้
Mike S

ฉันได้แก้ไขแล้วในกรณีที่คุณต้องการลบ -1 :) และใช่การตรวจสอบไดเรกทอรีข้ามแพลตฟอร์มอาจมีความซับซ้อนมากขึ้น แต่โดยทั่วไปคุณต้องการสร้าง / เขียนไปยังไฟล์ในไดเรกทอรีนั้นซึ่งในกรณีนี้ ตัวอย่างที่ฉันให้ไว้ยังควรใช้ หากมีปัญหาเกี่ยวกับสิทธิ์ไดเร็กทอรีบางอย่างเกิดขึ้นก็ควรจะยังคงส่ง IOError เมื่อพยายามเปิด filehandle
Rohaq

ฉันลบการโหวตลงคะแนน ขออภัยด้วยและขอขอบคุณสำหรับการสนับสนุนของคุณ
Mike S

ไม่ต้องกังวลคนที่ถามตอบยินดีเสมอ!
Rohaq

9

หากคุณสนใจเฉพาะไฟล์ perms os.access(path, os.W_OK)ควรทำในสิ่งที่คุณขอ หากคุณต้องการทราบว่าคุณสามารถเขียนลงในไดเร็กทอรีได้หรือopen()ไม่ไฟล์ทดสอบสำหรับการเขียน (ไม่ควรมีอยู่ก่อนหน้านี้) ตรวจจับและตรวจสอบIOErrorและล้างไฟล์ทดสอบในภายหลัง

โดยทั่วไปเพื่อหลีกเลี่ยงการโจมตีTOCTOU (เฉพาะปัญหาหากสคริปต์ของคุณทำงานด้วยสิทธิ์ที่สูงขึ้น - suid หรือ cgi หรือมากกว่านั้น) คุณไม่ควรไว้วางใจการทดสอบล่วงหน้าเหล่านี้ แต่ปล่อย privs ทำopen()และคาดหวัง ที่IOError.


7

ตรวจสอบบิตของโหมด:

def isWritable(name):
  uid = os.geteuid()
  gid = os.getegid()
  s = os.stat(dirname)
  mode = s[stat.ST_MODE]
  return (
     ((s[stat.ST_UID] == uid) and (mode & stat.S_IWUSR)) or
     ((s[stat.ST_GID] == gid) and (mode & stat.S_IWGRP)) or
     (mode & stat.S_IWOTH)
     )

4
โซลูชันนี้เป็น Unix เท่านั้น
Björn Lindqvist

4

นี่คือสิ่งที่ฉันสร้างขึ้นตามคำตอบของ ChristopheD:

import os

def isWritable(directory):
    try:
        tmp_prefix = "write_tester";
        count = 0
        filename = os.path.join(directory, tmp_prefix)
        while(os.path.exists(filename)):
            filename = "{}.{}".format(os.path.join(directory, tmp_prefix),count)
            count = count + 1
        f = open(filename,"w")
        f.close()
        os.remove(filename)
        return True
    except Exception as e:
        #print "{}".format(e)
        return False

directory = "c:\\"
if (isWritable(directory)):
    print "directory is writable"
else:
    print "directory is not writable"

3
 if os.access(path_to_folder, os.W_OK) is not True:
            print("Folder not writable")
 else :
            print("Folder writable")

สามารถดูข้อมูลเพิ่มเติมเกี่ยวกับการเข้าถึงได้ที่นี่


2
นี่คือสำเนาคำตอบของ Max Shawabkeh โดยมีกระดาษห่อเล็ก ๆ อยู่รอบ ๆ ทำให้เป็นการวางสำเนาอย่างรวดเร็ว แต่ควรเพิ่มลงในโพสต์ต้นฉบับของ Max
Jorrick Sleijster

1

ฉันพบกับความต้องการเดียวกันนี้ในขณะที่เพิ่มอาร์กิวเมนต์ผ่าน argparse บิวท์อินใช้type=FileType('w')ไม่ได้สำหรับฉันเพราะฉันกำลังมองหาไดเร็กทอรี ฉันลงเอยด้วยการเขียนวิธีการของตัวเองเพื่อแก้ปัญหาของฉัน นี่คือผลลัพธ์ที่มีข้อมูลโค้ด argparse

#! /usr/bin/env python
import os
import argparse

def writable_dir(dir):
    if os.access(dir, os.W_OK) and os.path.isdir(dir):
        return os.path.abspath(dir)
    else:
        raise argparse.ArgumentTypeError(dir + " is not writable or does not exist.")

parser = argparse.ArgumentParser()
parser.add_argument("-d","--dir", type=writable_dir(), default='/tmp/',
    help="Directory to use. Default: /tmp")
opts = parser.parse_args()

ซึ่งส่งผลดังต่อไปนี้:

$ python dir-test.py -h
usage: dir-test.py [-h] [-d DIR]

optional arguments:
  -h, --help         show this help message and exit
  -d DIR, --dir DIR  Directory to use. Default: /tmp

$ python dir-test.py -d /not/real
usage: dir-test.py [-h] [-d DIR]
dir-test.py: error: argument -d/--dir: /not/real is not writable or does not exist.

$ python dir-test.py -d ~

ฉันย้อนกลับไปและเพิ่มการพิมพ์ opts.dirต่อท้ายและทุกอย่างดูเหมือนจะทำงานได้ตามต้องการ


0

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

ข้อจำกัดความรับผิดชอบ - ไม่ทำงานบน Windows เนื่องจากไม่ได้ใช้รูปแบบการอนุญาต POSIX (และpwdโมดูลไม่พร้อมใช้งานที่นั่น) เช่น - โซลูชันสำหรับระบบ * nix เท่านั้น

โปรดทราบว่าไดเร็กทอรีต้องมีการตั้งค่าทั้งหมด 3 บิต - อ่านเขียนและ eXecute
ตกลง R ไม่ใช่สิ่งที่ต้องทำ แต่คุณไม่สามารถแสดงรายการในไดเร็กทอรีได้ (ดังนั้นคุณต้องรู้ชื่อ) ในทางกลับกันจำเป็นอย่างยิ่ง - หากไม่มีผู้ใช้ไม่สามารถอ่าน inodes ของไฟล์ได้ ดังนั้นแม้จะมี W โดยไม่มีไฟล์ X ก็ไม่สามารถสร้างหรือแก้ไขได้ คำอธิบายโดยละเอียดเพิ่มเติมที่ลิงค์นี้

สุดท้ายโหมดที่มีอยู่ในstatโมดูลของพวกเขารายละเอียดอยู่ในไอโหนด (7) ชาย

ตัวอย่างรหัสวิธีตรวจสอบ:

import pwd
import stat
import os

def check_user_dir(user, directory):
    dir_stat = os.stat(directory)

    user_id, group_id = pwd.getpwnam(user).pw_uid, pwd.getpwnam(user).pw_gid
    directory_mode = dir_stat[stat.ST_MODE]

    # use directory_mode as mask 
    if user_id == dir_stat[stat.ST_UID] and stat.S_IRWXU & directory_mode == stat.S_IRWXU:     # owner and has RWX
        return True
    elif group_id == dir_stat[stat.ST_GID] and stat.S_IRWXG & directory_mode == stat.S_IRWXG:  # in group & it has RWX
        return True
    elif stat.S_IRWXO & directory_mode == stat.S_IRWXO:                                        # everyone has RWX
        return True

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