Determinare se una directory è scrivibile


101

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:


185

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.


4
A seconda della situazione, "è più facile chiedere perdono" non è il modo migliore, anche in Python. A volte è consigliabile "chiedere il permesso" come con il metodo os.access () citato, ad esempio quando la probabilità di dover rilevare un errore è alta.
mjv

53
Testare una directory solo per il bit di scrittura non è sufficiente se si desidera scrivere file nella directory. Dovrai testare anche il bit di esecuzione se vuoi scrivere nella directory. os.access ('/ path / to / folder', os.W_OK | os.X_OK) Con os.W_OK da solo puoi solo eliminare la directory (e solo se quella directory è vuota)
fthinker

4
Un altro 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?')
Alexios

5
Forse un programma vuole solo sapere senza avere la necessità di scrivere effettivamente. Potrebbe semplicemente voler cambiare l'aspetto e / o il comportamento di una GUI in base alla proprietà. In tal caso non considererei pitonico scrivere ed eliminare un file solo come prova.
Bachsau

1
Appena testato su una condivisione di rete Windows. os.access(dirpath, os.W_OK | os.X_OK)restituisce True anche se non ho accesso in scrittura.
iamanigeeit

69

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.


5
+1 Python o no, questo è davvero il modo più affidabile per testare l'accesso.
John Knoeller

5
Questo si prende cura anche di altri errori che possono verificarsi durante la scrittura sul disco, ad esempio nessuno spazio su disco rimasto. Questo è il potere di provare .. non è necessario ricordare tutto ciò che può andare storto ;-)
Jochen Ritzel

4
Grazie ragazzi. Ho deciso di utilizzare os.access poiché la velocità è un fattore importante in quello che sto facendo qui, anche se posso certamente capire i pregi di "è più facile chiedere perdono che permesso". ;)
tigre illuminata

4
È un ottimo IDIO ... m - specialmente se abbinato a un altro idioma 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?
Tomasz Gandor

4
Forse un programma vuole solo sapere senza avere la necessità di scrivere effettivamente. Potrebbe semplicemente voler cambiare l'aspetto e / o il comportamento di una GUI in base alla proprietà. In tal caso non considererei pitonico scrivere ed eliminare un file solo come prova.
Bachsau

19

La mia soluzione utilizzando il tempfilemodulo:

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.


1
Penso che quello che usa tempfile sia il pulitore perché di sicuro non lascia residui.
cavalletta

3
questo metodo non funziona utilizzando tempfile. funziona solo quando non c'è OSErrorsignificato ha il permesso di scrivere / cancellare. altrimenti non lo farà return Falseperché 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?

10

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.


1
Questo vale per un file, non per una directory che è ciò che l'OP ha chiesto. È possibile avere un file in una directory e la directory non è scrivibile ma il file stesso lo è, se il file esiste già. Questo può essere importante nell'amministrazione dei sistemi dove ad esempio stai creando file di log che vuoi che esistano già ma non vuoi che le persone usino una directory di log per lo spazio temporaneo.
Mike S

... e in realtà l'ho bocciato, il che ora penso sia stato un errore. Ci sono problemi, come ha detto Rohaq, con le condizioni di gara. Ci sono altri problemi su varie piattaforme in cui puoi testare la directory e sembra scrivibile, ma in realtà non lo è. L'esecuzione di controlli scrivibili su directory multipiattaforma è più difficile di quanto sembri. Quindi, fintanto che sei consapevole dei problemi, questa potrebbe essere una tecnica eccellente. Lo stavo guardando da una prospettiva troppo UNIX-y, che è un mio errore. Qualcuno modifica questa risposta in modo che io possa rimuovere il mio -1.
Mike S

L'ho modificato, nel caso in cui desideri rimuovere -1 :) E sì, i controlli delle directory multipiattaforma possono diventare più complicati, ma in genere stai cercando di creare / scrivere su un file in quella directory, nel qual caso l'esempio che ho fornito dovrebbe essere ancora valido. Se si verificano problemi relativi ai permessi di directory, dovrebbe comunque lanciare un errore IOError quando si tenta di aprire il filehandle.
Rohaq

Ho rimosso il mio voto negativo. Mi dispiace e grazie per il tuo contributo.
Mike S

Nessun problema, le persone che chiedono risposte sono sempre benvenute!
Rohaq

9

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 IOErrore 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.


7

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

4
Questa soluzione è solo Unix.
Björn Lindqvist

4

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"

3
 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


2
Questa è fondamentalmente una copia della risposta di Max Shawabkeh con un piccolo involucro attorno ad essa. Rende un rapido copia incolla ma un'idea migliore sarebbe quella di averlo aggiunto al post originale di Max.
Jorrick Sleijster

1

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.


0

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 pwdmodulo 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 pwdmodulo 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 statmodulo, 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.