Каким будет лучший способ в Python определить, доступен ли каталог для записи пользователю, выполняющему сценарий? Поскольку это, вероятно, будет связано с использованием модуля os, я должен упомянуть, что запускаю его в среде * nix.
Каким будет лучший способ в Python определить, доступен ли каталог для записи пользователю, выполняющему сценарий? Поскольку это, вероятно, будет связано с использованием модуля os, я должен упомянуть, что запускаю его в среде * nix.
Ответы:
Хотя то, что предложил Кристоф, является более питоническим решением, в модуле os есть функция 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 я вижу, что действительно существует проблема при использовании там временного файла , см. Issue22107: модуль временного файла неверно интерпретирует ошибку отказа в доступе в 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, работает как при разрешении, так и при отказе в разрешении. Помогите?
Наткнулся на эту ветку в поисках примеров для кого-то. Первый результат в 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")
Это пытается открыть дескриптор файла для записи и завершается с ошибкой, если указанный файл не может быть записан: это намного легче читать, и это гораздо лучший способ сделать это, чем выполнять предварительную проверку пути к файлу или каталогу. , поскольку это позволяет избежать условий гонки; случаи, когда файл становится недоступным для записи между временем, когда вы запускаете предварительную проверку, и когда вы фактически пытаетесь записать в файл.
Если вас интересует только файл с завивками, os.access(path, os.W_OK)
следует делать то, о чем вы просите. Если вместо этого вы хотите узнать, можете ли вы записать в каталог open()
тестовый файл для записи (он не должен существовать заранее), поймайте и изучите любой IOError
, а затем очистите тестовый файл.
В более общем плане, чтобы избежать атак TOCTOU (проблема только в том случае, если ваш скрипт работает с повышенными привилегиями - suid или cgi или около того), вам не следует на самом деле доверять этим заблаговременным тестам, а отказаться от привилегий, выполнить 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 ~
Я вернулся и добавил print opts.dir до конца, и, похоже, все работает как нужно.
Если вам нужно проверить разрешение другого пользователя (да, я понимаю, что это противоречит вопросу, но может кому-то пригодиться), вы можете сделать это через pwd
модуль и биты режима каталога.
Отказ от ответственности - не работает в Windows, так как не использует модель разрешений POSIX (и pwd
модуль там недоступен), например - решение только для систем * nix.
Обратите внимание, что в каталоге должны быть установлены все 3 бита - чтение, запись и eXecute.
Хорошо, R не является абсолютной необходимостью, но без него вы не можете перечислить записи в каталоге (так что вы должны знать их имена). Execute, с другой стороны, абсолютно необходимо - без него пользователь не может читать inodes файла; поэтому даже имея W, без X файлы не могут быть созданы или изменены. Более подробное объяснение по этой ссылке.
Наконец, режимы доступны в 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