Quale sarebbe il modo migliore in Python per determinare se una directory è scrivibile per l'utente che esegue lo script? Dato che questo probabilmente implicherà l'utilizzo del modulo os, dovrei menzionare che lo sto eseguendo in un ambiente * nix.
Quale sarebbe il modo migliore in Python per determinare se una directory è scrivibile per l'utente che esegue lo script? Dato che questo probabilmente implicherà l'utilizzo del modulo os, dovrei menzionare che lo sto eseguendo in un ambiente * nix.
Risposte:
Sebbene ciò che Christophe ha suggerito sia una soluzione più pitonica, il modulo os ha la funzione os.access per controllare l'accesso:
os.access('/path/to/folder', os.W_OK)
# W_OK è per scrivere, R_OK per leggere, ecc.
os.access()
problema è che controlla utilizzando l' UID e il GID reali , non quelli effettivi . Ciò potrebbe causare stranezze negli ambienti SUID / SGID. ('ma lo script esegue setuid root, perché non può scrivere sul file?')
os.access(dirpath, os.W_OK | os.X_OK)
restituisce True anche se non ho accesso in scrittura.
Può sembrare strano suggerirlo, ma un idioma comune di Python lo è
È più facile chiedere perdono che permesso
Seguendo questo idioma, si potrebbe dire:
Prova a scrivere nella directory in questione e individua l'errore se non sei autorizzato a farlo.
except: pass
- in questo modo puoi sempre essere ottimista e pensare bene a te stesso. / sarcasmo spento. Ora perché dovrei, ad esempio, provare a scrivere qualcosa in ogni directory del mio filesystem, per produrre un elenco di posizioni scrivibili?
La mia soluzione utilizzando il tempfile
modulo:
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
Aggiornamento: dopo aver testato nuovamente il codice su Windows, vedo che c'è effettivamente un problema quando si utilizza il file temp lì, vedere il problema 22107: il modulo tempfile interpreta erroneamente l'errore di accesso negato su Windows . Nel caso di una directory non scrivibile, il codice si blocca per diversi secondi e infine lancia un IOError: [Errno 17] No usable temporary file name found
. Forse questo è ciò che user2171842 stava osservando? Sfortunatamente il problema non è stato risolto per ora, quindi per gestirlo, è necessario rilevare anche l'errore:
except (OSError, IOError) as e:
if e.errno == errno.EACCES or e.errno == errno.EEXIST: # 13, 17
Il ritardo è ovviamente ancora presente in questi casi quindi.
tempfile
. funziona solo quando non c'è OSError
significato ha il permesso di scrivere / cancellare. altrimenti non lo farà return False
perché non viene restituito alcun errore e lo script non continuerà a essere eseguito o terminato. nulla viene restituito. è solo bloccato su quella linea. tuttavia, la creazione di un file non temporaneo come la risposta di khattam funziona sia quando l'autorizzazione è consentita o negata. Aiuto?
Sono incappato in questo thread alla ricerca di esempi per qualcuno. Primo risultato su Google, complimenti!
Le persone parlano del modo pitonico di farlo in questo thread, ma non semplici esempi di codice? Ecco qua, per chiunque altro inciampi in:
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")
Questo tenta di aprire un filehandle per la scrittura ed esce con un errore se il file specificato non può essere scritto: Questo è molto più facile da leggere ed è un modo molto migliore di farlo piuttosto che eseguire controlli preliminari sul percorso del file o sulla directory , in quanto evita le condizioni di gara; casi in cui il file diventa non scrivibile tra il momento in cui si esegue il controllo preliminare e il momento in cui si tenta effettivamente di scrivere sul file.
Se ti interessano solo i file permanenti, os.access(path, os.W_OK)
dovresti fare quello che chiedi. Se invece vuoi sapere se puoi scrivere nella directory, open()
un file di test per la scrittura (non dovrebbe esistere in anticipo), catturarne ed esaminarne uno IOError
e ripulire il file di test in seguito.
Più in generale, per evitare attacchi TOCTOU (solo un problema se il tuo script viene eseguito con privilegi elevati - suid o cgi o giù di lì), non dovresti davvero fidarti di questi test anticipati, ma abbandonare privs, fare il open()
e aspettarsi il IOError
.
Controlla i bit di modalità:
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)
)
Ecco qualcosa che ho creato sulla base della risposta di 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")
maggiori informazioni sull'accesso possono essere trovate qui
Mi sono imbattuto in questa stessa esigenza durante l'aggiunta di un argomento tramite argparse. Il built-in type=FileType('w')
non funzionava per me dato che stavo cercando una directory. Ho finito per scrivere il mio metodo per risolvere il mio problema. Ecco il risultato con argparse snippet.
#! /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()
Ciò si traduce nel seguente:
$ 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 ~
Sono tornato indietro e ho aggiunto print opts.dir alla fine, e tutto sembra funzionare come desiderato.
Se hai bisogno di controllare il permesso di un altro utente (sì, mi rendo conto che questo contraddice la domanda, ma potrebbe tornare utile per qualcuno), puoi farlo attraverso il pwd
modulo e i bit di modalità della directory.
Dichiarazione di non responsabilità - non funziona su Windows, poiché non utilizza il modello di autorizzazioni POSIX (e il pwd
modulo non è disponibile lì), ad esempio - soluzione solo per sistemi * nix.
Notare che una directory deve avere tutti e 3 i bit impostati: lettura, scrittura ed eXecute.
Ok, R non è un must assoluto, ma senza di esso non puoi elencare le voci nella directory (quindi devi conoscere i loro nomi). D'altra parte, l'esecuzione è assolutamente necessaria - senza di essa l'utente non può leggere gli inode del file; quindi anche con W, senza X i file non possono essere creati o modificati. Spiegazione più dettagliata a questo link.
Infine, le modalità sono disponibili nel stat
modulo, le loro descrizioni sono in inode (7) man .
Codice di esempio come controllare:
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