确定目录是否可写


101

在Python中确定执行脚本的用户是否可写目录的最佳方法是什么?因为这可能涉及使用os模块,所以我应该提到我是在* nix环境下运行它的。

Answers:


185

尽管Christophe建议的是更Python化的解决方案,但os模块确实具有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 2012年

4
另一个难题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习惯用法是

寻求宽恕比获得许可要容易

遵循这一习语,人们可能会说:

尝试写入有问题的目录,如果没有权限,则捕获错误。


5
是否使用+1 Python,这实际上是测试访问权限的最可靠方法。
约翰·诺勒

5
这还可以解决写入磁盘时可能发生的其他错误-例如,没有剩余磁盘空间。那就是尝试的力量。.您不需要记住所有可能出错的地方;-)
Jochen Ritzel 2010年

4
多谢你们。决定与os.access一起使用,因为速度是我在这里进行操作的重要因素,尽管我当然可以理解“请求宽恕比获得许可容易”的优点。;)
illuminatedtiger 2010年

4
这是一个很棒的IDIO ... m-尤其是当与另一个习惯用法结合使用时except: pass-这样,您可以始终保持乐观并高度评价自己。/讽刺。现在,为什么我要(例如,尝试在文件系统中的每个目录中写入一些内容)以生成可写位置列表?
Tomasz Gandor

4
也许程序只是想知道而无需实际编写。它可能只想根据该属性更改GUI的外观和/或行为。在那种情况下,我不会像测试一样将其写入和删除文件视为pythonic。
巴查(Bachsau)'18年

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。也许这是user2171842正在观察的内容?不幸的是,该问题暂时无法解决,因此要解决此问题,还必须捕获该错误:

    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上获得了第一个结果!

人们谈论在此线程中使用Python的方式,但是没有简单的代码示例吗?在这里,对于任何偶然发现的人:

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")

这会尝试打开文件句柄进行写入,如果指定的文件无法写入,则会退出并返回错误:这更容易阅读,并且比对文件路径或目录进行预检查要好得多,因为它避免了比赛条件;在运行预检查的时间到实际尝试写入文件之间文件不可写的情况。


1
这适用于文件,而不是OP要求的目录。您可以在目录中有一个文件,并且该目录不可写,但文件本身是可以写的(如果文件已经存在)。这在系统管理中可能很重要,在该系统管理中,例如您要创建要已经存在但不希望人们使用日志目录作为临时空间的日志文件。
Mike S

...实际上我投了反对票,现在我认为这是一个错误。正如罗哈克(Rohaq)所提到的,竞赛条件存在问题。在各种平台上还有其他问题,您可以在其中测试目录,它看起来可写,但实际上不是。执行跨平台目录可写检查比看起来困难。因此,只要您知道这些问题,这可能是一种很好的技术。我从UNIX-y的角度来看它,这是我的错误。有人编辑此答案,所以我可以删除-1。
Mike S

我已经对其进行了编辑,以防万一您想要删除-1 :)是的,跨平台目录检查可能会变得更加复杂,但是通常您希望在该目录中创建/写入文件-在这种情况下我给出的示例仍然适用。如果出现一些与目录权限有关的问题,则在尝试打开文件句柄时仍应抛出IOError。
Rohaq

我删除了我的选票。抱歉,谢谢您的贡献。
Mike S

不用担心,人们总是质疑答案!
Rohaq

9

如果您只关心文件烫发,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。
比昂·林德奎斯特

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 '17

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 ~

回过头来,在最后添加了print opts.dir,一切似乎都可以正常运行了。


0

如果您需要检查其他用户的权限(是的,我知道这与问题相矛盾,但可能对某人有用),则可以通过pwd模块和目录的模式位来进行检查。

免责声明 -在Windows上不起作用,因为它不使用POSIX权限模型(并且该pwd模块在那里不可用),例如-仅针对* nix系统的解决方案。

请注意,目录必须设置所有3位-读,写和eXecute。
好的,R不是绝对必须的,但是没有,您不能在目录中列出条目(因此您必须知道它们的名称)。另一方面,绝对需要执行-没有用户无法读取文件的inode;因此即使没有W也无法创建或修改W。在此链接上有更详细的说明。

最后,这些模式在stat模块中可用,其描述在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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.