【发布时间】:2011-01-07 23:28:06
【问题描述】:
在 Python 中,确定目录是否可供执行脚本的用户写入的最佳方法是什么?由于这可能涉及使用 os 模块,我应该提到我在 *nix 环境下运行它。
【问题讨论】:
标签: python file permissions directory operating-system
在 Python 中,确定目录是否可供执行脚本的用户写入的最佳方法是什么?由于这可能涉及使用 os 模块,我应该提到我在 *nix 环境下运行它。
【问题讨论】:
标签: python file permissions directory operating-system
这似乎很奇怪,但一个常见的 Python 习惯用法是
请求原谅更容易 不是为了许可
按照这个成语,人们可能会说:
尝试写入有问题的目录,如果您没有这样做的权限,请捕获错误。
【讨论】:
except: pass 结合使用时 - 这样您就可以始终保持乐观并高度评价自己。 /讽刺关闭。现在我为什么要这样做,例如尝试在我的文件系统中的每个目录中写入一些内容,以生成可写位置列表?
检查模式位:
import os, stat
def isWritable(dirname):
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)
)
【讨论】:
虽然 Christophe 建议的是一个更 Pythonic 的解决方案,但 os 模块确实有 the os.access function 来检查访问:
os.access('/path/to/folder', os.W_OK) # W_OK 用于写入,R_OK 用于读取等
【讨论】:
os.access() 的另一个问题是它使用 real UID 和 GID,而不是 有效 进行检查。这可能会导致 SUID/SGID 环境中的怪异。 (‘但是脚本运行setuid root,为什么不能写入文件?’)
os.access(dirpath, os.W_OK | os.X_OK) 即使我没有写权限也返回 True。
如果您只关心文件权限,os.access(path, os.W_OK) 应该满足您的要求。如果您想知道是否可以写入目录,open() 一个用于写入的测试文件(它不应该事先存在),捕获并检查任何IOError,然后清理之后测试文件。
更一般地说,为了避免TOCTOU 攻击(只有当您的脚本以提升的权限运行时才会出现问题 - suid 或 cgi 左右),您不应该真正信任这些提前测试,而是放弃 privs,执行open(),并期待IOError。
【讨论】:
这是我根据 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"
【讨论】:
偶然发现此线程正在寻找某人的示例。 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")
这会尝试打开一个文件句柄进行写入,如果指定的文件无法写入则退出并返回错误:这更容易阅读,并且是一种更好的方法,而不是对文件路径进行预检查或目录,因为它避免了竞争条件;在运行预检查和实际尝试写入文件之间文件变得不可写的情况。
【讨论】:
我使用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 module misinterprets access denied error on Windows。
在不可写目录的情况下,代码会挂起几秒钟,最后抛出IOError: [Errno 17] No usable temporary file name found。也许这就是 user2171842 观察到的?
不幸的是,这个问题目前还没有解决,所以要处理这个问题,还需要捕获错误:
except (OSError, IOError) as e:
if e.errno == errno.EACCES or e.errno == errno.EEXIST: # 13, 17
那么,在这些情况下,延迟当然仍然存在。
【讨论】:
tempfile 时不起作用。它仅在没有OSError 时才有效,这意味着它有权写入/删除。否则不会return False,因为没有返回错误,脚本不会继续执行或退出。什么都没有返回。它只是停留在那条线上。但是,创建非临时文件(例如 khattam 的答案)在允许或拒绝许可时都有效。帮忙?
OSError: [Errno 30] Read-only file system
if os.access(path_to_folder, os.W_OK) is not True:
print("Folder not writable")
else :
print("Folder writable")
更多关于访问的信息可以找到它here
【讨论】:
如果您需要检查另一个用户的权限(是的,我意识到这与问题相矛盾,但可能对某人有用),您可以通过pwd 模块完成,和目录的模式位。
免责声明 - 不适用于 Windows,因为它不使用 POSIX 权限模型(并且 pwd 模块不可用),例如- 仅适用于 *nix 系统的解决方案。
请注意,目录必须设置所有 3 位 - 读取、写入和执行。
好的,R 不是绝对必须的,但是如果没有它,您就无法列出目录中的条目(因此您必须知道它们的名称)。另一方面,绝对需要执行 - 用户无法读取文件的 inode;所以即使有 W,没有 X 文件也无法创建或修改。 More detailed explanation at this link.
最后,这些模式在stat 模块中可用,它们的descriptions are in inode(7) man。
示例代码如何检查:
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
【讨论】:
我在通过 argparse 添加参数时遇到了同样的需求。内置的type=FileType('w') 对我不起作用,因为我正在寻找一个目录。我最终编写了自己的方法来解决我的问题。这是 argparse sn-p 的结果。
#! /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 ~
我返回并在末尾添加了 print opts.dir,一切似乎都按预期运行。
【讨论】: