วิธีใดเป็นวิธีที่ดีที่สุดใน Python ในการพิจารณาว่าไดเร็กทอรีสามารถเขียนได้สำหรับผู้ใช้ที่เรียกใช้สคริปต์หรือไม่ เนื่องจากสิ่งนี้อาจเกี่ยวข้องกับการใช้โมดูลระบบปฏิบัติการฉันควรพูดถึงว่าฉันใช้งานภายใต้สภาพแวดล้อม * nix
วิธีใดเป็นวิธีที่ดีที่สุดใน Python ในการพิจารณาว่าไดเร็กทอรีสามารถเขียนได้สำหรับผู้ใช้ที่เรียกใช้สคริปต์หรือไม่ เนื่องจากสิ่งนี้อาจเกี่ยวข้องกับการใช้โมดูลระบบปฏิบัติการฉันควรพูดถึงว่าฉันใช้งานภายใต้สภาพแวดล้อม * nix
คำตอบ:
แม้ว่าสิ่งที่ Christophe แนะนำจะเป็นโซลูชัน Pythonic มากกว่า แต่โมดูลระบบปฏิบัติการจะมีฟังก์ชัน os.accessเพื่อตรวจสอบการเข้าถึง:
os.access('/path/to/folder', os.W_OK)
# W_OK ใช้สำหรับเขียน R_OK สำหรับการอ่าน ฯลฯ
os.access()
คือการตรวจสอบโดยใช้จริง UID และ GID ไม่ได้มีประสิทธิภาพคน สิ่งนี้อาจทำให้เกิดความแปลกในสภาพแวดล้อม SUID / SGID ('แต่สคริปต์เรียกใช้ setuid root ทำไมจึงไม่สามารถเขียนลงในไฟล์ได้')
os.access(dirpath, os.W_OK | os.X_OK)
ส่งคืน True แม้ว่าฉันจะไม่มีสิทธิ์เขียน
อาจดูแปลกที่จะแนะนำสิ่งนี้ แต่สำนวน Python ทั่วไปคือ
การขอการอภัยนั้นง่ายกว่าการขออนุญาต
ตามสำนวนนั้นอาจมีคนพูดว่า:
ลองเขียนลงในไดเร็กทอรีที่เป็นปัญหาและตรวจจับข้อผิดพลาดหากคุณไม่มีสิทธิ์
except: pass
วิธีนี้คุณจะสามารถมองโลกในแง่ดีและคิดว่าตัวเองสูง / ปิดการเสียดสี ตอนนี้ทำไมฉันถึงต้องการเช่นพยายามเขียนบางสิ่งลงในทุกไดเร็กทอรีในระบบไฟล์ของฉันเพื่อสร้างรายการตำแหน่งที่เขียนได้
วิธีแก้ปัญหาของฉันโดยใช้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
แน่นอนว่าความล่าช้ายังคงปรากฏอยู่ในกรณีเหล่านี้
tempfile
วิธีการนี้จะไม่ทำงานโดยใช้ ใช้งานได้ก็ต่อเมื่อไม่มีOSError
ความหมายที่ได้รับอนุญาตให้เขียน / ลบ มิฉะนั้นจะไม่ใช่return False
เพราะไม่มีการส่งคืนข้อผิดพลาดและสคริปต์จะไม่ดำเนินการต่อหรือออก ไม่มีอะไรจะกลับมา มันติดอยู่ที่เส้นนั้น อย่างไรก็ตามการสร้างไฟล์ที่ไม่ใช่ชั่วคราวเช่นคำตอบของ khattam จะทำงานได้ทั้งเมื่ออนุญาตหรือปฏิเสธ ช่วยด้วย?
สะดุดในหัวข้อนี้กำลังค้นหาตัวอย่างสำหรับใครบางคน ผลลัพธ์แรกใน 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 และเมื่อคุณพยายามเขียนลงไฟล์จริงๆ
หากคุณสนใจเฉพาะไฟล์ perms os.access(path, os.W_OK)
ควรทำในสิ่งที่คุณขอ หากคุณต้องการทราบว่าคุณสามารถเขียนลงในไดเร็กทอรีได้หรือopen()
ไม่ไฟล์ทดสอบสำหรับการเขียน (ไม่ควรมีอยู่ก่อนหน้านี้) ตรวจจับและตรวจสอบIOError
และล้างไฟล์ทดสอบในภายหลัง
โดยทั่วไปเพื่อหลีกเลี่ยงการโจมตีTOCTOU (เฉพาะปัญหาหากสคริปต์ของคุณทำงานด้วยสิทธิ์ที่สูงขึ้น - suid หรือ cgi หรือมากกว่านั้น) คุณไม่ควรไว้วางใจการทดสอบล่วงหน้าเหล่านี้ แต่ปล่อย privs ทำopen()
และคาดหวัง ที่IOError
.
ตรวจสอบบิตของโหมด:
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)
)
นี่คือสิ่งที่ฉันสร้างขึ้นตามคำตอบของ 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"
if os.access(path_to_folder, os.W_OK) is not True:
print("Folder not writable")
else :
print("Folder writable")
สามารถดูข้อมูลเพิ่มเติมเกี่ยวกับการเข้าถึงได้ที่นี่
ฉันพบกับความต้องการเดียวกันนี้ในขณะที่เพิ่มอาร์กิวเมนต์ผ่าน 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ต่อท้ายและทุกอย่างดูเหมือนจะทำงานได้ตามต้องการ
หากคุณต้องการตรวจสอบการอนุญาตของผู้ใช้รายอื่น (ใช่ฉันรู้ว่าสิ่งนี้ขัดแย้งกับคำถาม แต่อาจมีประโยชน์สำหรับใครบางคน) คุณสามารถทำได้ผ่าน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