Feststellen, ob ein Verzeichnis beschreibbar ist

101

Wie kann in Python am besten festgestellt werden, ob ein Verzeichnis für den Benutzer, der das Skript ausführt, beschreibbar ist? Da dies wahrscheinlich die Verwendung des OS-Moduls beinhaltet, sollte ich erwähnen, dass ich es unter einer * nix-Umgebung ausführe.

beleuchteter Tiger
quelle

Antworten:

185

Obwohl Christophe eine pythonischere Lösung vorgeschlagen hat, verfügt das OS- Modul über die Funktion os.access , um den Zugriff zu überprüfen:

os.access('/path/to/folder', os.W_OK) # W_OK ist zum Schreiben, R_OK zum Lesen usw.

Max Shawabkeh
quelle
4
Je nach Situation ist das "leichter um Vergebung bitten" nicht der beste Weg, selbst in Python. Manchmal ist es ratsam, wie bei der genannten Methode os.access () um Erlaubnis zu bitten, beispielsweise wenn die Wahrscheinlichkeit hoch ist, einen Fehler abfangen zu müssen.
MJV
53
Das Testen eines Verzeichnisses nur auf das Schreibbit reicht nicht aus, wenn Sie Dateien in das Verzeichnis schreiben möchten. Sie müssen auch auf das Ausführungsbit testen, wenn Sie in das Verzeichnis schreiben möchten. os.access ('/ path / to / folder', os.W_OK | os.X_OK) Mit os.W_OK allein können Sie das Verzeichnis nur löschen (und nur, wenn dieses Verzeichnis leer ist)
fthinker
4
Ein weiteres Problem os.access()ist die Überprüfung anhand der tatsächlichen UID und GID, nicht anhand der effektiven . Dies kann in SUID / SGID-Umgebungen zu Verrücktheiten führen. ('aber das Skript läuft setuid root, warum kann es nicht in die Datei schreiben?')
Alexios
5
Vielleicht möchte ein Programm nur wissen, ohne dass es tatsächlich schreiben muss. Möglicherweise möchten Sie nur das Aussehen und / oder Verhalten einer GUI entsprechend der Eigenschaft ändern. In diesem Fall würde ich es nicht als pythonisch betrachten, eine Datei nur als Test zu schreiben und zu löschen.
Bachsau
1
Gerade auf einer Windows-Netzwerkfreigabe getestet. os.access(dirpath, os.W_OK | os.X_OK)Gibt True zurück, auch wenn ich keinen Schreibzugriff habe.
iamanigeeit
69

Es mag seltsam erscheinen, dies vorzuschlagen, aber eine verbreitete Python-Sprache ist

Es ist einfacher, um Vergebung zu bitten als um Erlaubnis

Nach dieser Redewendung könnte man sagen:

Versuchen Sie, in das betreffende Verzeichnis zu schreiben, und fangen Sie den Fehler ab, wenn Sie nicht die Berechtigung dazu haben.

ChristopheD
quelle
5
+1 Python oder nicht, dies ist wirklich die zuverlässigste Methode, um den Zugriff zu testen.
John Knoeller
5
Dies behebt auch andere Fehler, die beim Schreiben auf die Festplatte auftreten können - beispielsweise kein Speicherplatz mehr. Das ist die Kraft des Versuchens. Sie müssen sich nicht an alles erinnern, was schief gehen kann ;-)
Jochen Ritzel
4
Danke Leute. Die Entscheidung für os.access als Geschwindigkeit ist ein wichtiger Faktor für das, was ich hier mache, obwohl ich die Vorzüge in "Es ist einfacher, um Vergebung als um Erlaubnis zu bitten" sicherlich verstehen kann. ;)
Illuminatedtiger
4
Es ist ein großartiges IDIO ... m - besonders wenn es mit einem anderen Idiom gekoppelt ist except: pass- auf diese Weise können Sie immer optimistisch sein und hoch von sich selbst denken. / Sarkasmus aus. Warum sollte ich nun beispielsweise versuchen, etwas in jedes Verzeichnis in meinem Dateisystem zu schreiben, um eine Liste der beschreibbaren Speicherorte zu erstellen?
Tomasz Gandor
4
Vielleicht möchte ein Programm nur wissen, ohne dass es tatsächlich schreiben muss. Möglicherweise möchten Sie nur das Aussehen und / oder Verhalten einer GUI entsprechend der Eigenschaft ändern. In diesem Fall würde ich es nicht als pythonisch betrachten, eine Datei nur als Test zu schreiben und zu löschen.
Bachsau
19

Meine Lösung mit dem tempfileModul:

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

Update: Nach dem erneuten Testen des Codes unter Windows stelle ich fest, dass bei der Verwendung von tempfile tatsächlich ein Problem vorliegt. Siehe Problem 22107: Das tempfile-Modul interpretiert den Fehler "Zugriff verweigert" unter Windows falsch . Bei einem nicht beschreibbaren Verzeichnis bleibt der Code einige Sekunden lang hängen und löst schließlich ein aus IOError: [Errno 17] No usable temporary file name found. Vielleicht hat user2171842 dies beobachtet? Leider ist das Problem vorerst nicht behoben. Um dies zu beheben, muss auch der Fehler abgefangen werden:

    except (OSError, IOError) as e:
        if e.errno == errno.EACCES or e.errno == errno.EEXIST:  # 13, 17

Die Verzögerung ist dann natürlich in diesen Fällen noch vorhanden.

zak
quelle
1
Ich denke, derjenige, der tempfile verwendet, ist der Reiniger, weil er sicher keine Rückstände hinterlässt.
Heuschrecke
3
Diese Methode funktioniert nicht mit tempfile. Es funktioniert nur, wenn es keine OSErrorBedeutung hat, dass es die Berechtigung zum Schreiben / Löschen hat. Andernfalls wird dies return Falsenicht der Fall sein, da kein Fehler zurückgegeben wird und das Skript nicht weiter ausgeführt oder beendet wird. nichts wird zurückgegeben. es steckt nur in dieser Zeile. Das Erstellen einer nicht temporären Datei wie der Antwort von khattam funktioniert jedoch sowohl, wenn die Berechtigung zulässig ist oder verweigert wird. Hilfe?
10

Stolperte über diesen Thread und suchte nach Beispielen für jemanden. Erstes Ergebnis bei Google, herzlichen Glückwunsch!

In diesem Thread wird über die pythonische Vorgehensweise gesprochen, aber keine einfachen Codebeispiele? Auf geht's für alle anderen, die stolpern:

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

Dies versucht, ein Dateihandle zum Schreiben zu öffnen, und wird mit einem Fehler beendet, wenn die angegebene Datei nicht beschrieben werden kann: Dies ist viel einfacher zu lesen und eine viel bessere Methode, als Vorprüfungen des Dateipfads oder des Verzeichnisses durchzuführen , da es Rennbedingungen vermeidet; Fälle, in denen die Datei zwischen dem Ausführen der Vorprüfung und dem tatsächlichen Versuch, in die Datei zu schreiben, nicht mehr beschreibbar ist.

Rohaq
quelle
1
Dies gilt für eine Datei, nicht für ein Verzeichnis, nach dem das OP gefragt hat. Sie können eine Datei in einem Verzeichnis haben und das Verzeichnis nicht beschreibbar sein, aber die Datei selbst ist, falls die Datei bereits vorhanden ist. Dies kann in der Systemadministration wichtig sein, wenn Sie beispielsweise Protokolldateien erstellen, die bereits vorhanden sein sollen, aber nicht möchten, dass Benutzer ein Protokollverzeichnis für temporären Speicherplatz verwenden.
Mike S
... und eigentlich habe ich es abgelehnt, was ich jetzt für einen Fehler halte. Wie Rohaq erwähnte, gibt es Probleme mit den Rennbedingungen. Es gibt andere Probleme auf verschiedenen Plattformen, auf denen Sie das Verzeichnis testen können, und es sieht beschreibbar aus, ist es aber nicht. Das Durchführen plattformübergreifender beschreibbarer Überprüfungen ist schwieriger als es aussieht. Solange Sie sich der Probleme bewusst sind, kann dies eine gute Technik sein. Ich habe es aus einer zu UNIX-y-Perspektive betrachtet, was mein Fehler ist. Jemand bearbeitet diese Antwort, damit ich meine -1 entfernen kann.
Mike S
Ich habe es bearbeitet, falls Sie die -1 entfernen möchten :) Und ja, plattformübergreifende Verzeichnisprüfungen können komplizierter werden, aber im Allgemeinen möchten Sie eine Datei in diesem Verzeichnis erstellen / in diese schreiben - in diesem Fall Das Beispiel, das ich gegeben habe, sollte immer noch gelten. Wenn ein Problem mit der Verzeichnisberechtigung auftritt, sollte beim Versuch, das Dateihandle zu öffnen, immer noch ein IOError ausgelöst werden.
Rohaq
Ich habe meine Ablehnung entfernt. Entschuldigung und vielen Dank für Ihren Beitrag.
Mike S
Keine Sorge, Leute, die Antworten hinterfragen, sind immer willkommen!
Rohaq
9

Wenn Sie sich nur für die Dauerwellen interessieren, os.access(path, os.W_OK)sollten Sie das tun, wonach Sie fragen. Wenn Sie stattdessen wissen möchten, ob Sie in das Verzeichnis schreiben können , open()eine Testdatei zum Schreiben (sie sollte vorher nicht vorhanden sein), eine abfangen und untersuchen IOErrorund anschließend die Testdatei bereinigen.

Um TOCTOU- Angriffe zu vermeiden (nur ein Problem, wenn Ihr Skript mit erhöhten Berechtigungen ausgeführt wird - suid oder cgi oder so), sollten Sie diesen vorzeitigen Tests nicht wirklich vertrauen, sondern Privilegien löschen, das tun open()und erwarten die IOError.

sverkerw
quelle
7

Überprüfen Sie die Modusbits:

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)
     )
Joe Koberg
quelle
4
Diese Lösung ist nur Unix.
Björn Lindqvist
4

Folgendes habe ich basierend auf der Antwort von ChristopheD erstellt:

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"
Khattam
quelle
3
 if os.access(path_to_folder, os.W_OK) is not True:
            print("Folder not writable")
 else :
            print("Folder writable")

Weitere Informationen zum Zugang finden Sie hier

Softmixt
quelle
2
Dies ist im Grunde eine Kopie von Max Shawabkehs Antwort mit einem kleinen Umschlag. Macht es ein schnelles Kopieren und Einfügen, aber eine bessere Idee wäre, es dem ursprünglichen Beitrag von Max hinzuzufügen.
Jorrick Sleijster
1

Ich bin auf dasselbe Bedürfnis gestoßen, als ich über argparse ein Argument hinzugefügt habe. Das eingebaute type=FileType('w')würde für mich nicht funktionieren, da ich nach einem Verzeichnis suchte. Am Ende schrieb ich meine eigene Methode, um mein Problem zu lösen. Hier ist das Ergebnis mit 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()

Das ergibt folgendes:

$ 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 ~

Ich ging zurück und fügte am Ende print opts.dir hinzu , und alles scheint wie gewünscht zu funktionieren.

796m9XfYTkmp
quelle
0

Wenn Sie die Berechtigung eines anderen Benutzers überprüfen müssen (ja, mir ist klar, dass dies der Frage widerspricht, aber für jemanden nützlich sein kann), können Sie dies über das pwdModul und die Modusbits des Verzeichnisses tun .

Haftungsausschluss - funktioniert nicht unter Windows, da das POSIX-Berechtigungsmodell nicht verwendet wird (und das pwdModul dort nicht verfügbar ist), z. B. - Lösung nur für * nix-Systeme.

Beachten Sie, dass in einem Verzeichnis alle 3 Bits gesetzt sein müssen - Lesen, Schreiben und eXecute.
Ok, R ist kein absolutes Muss, aber ohne es können Sie die Einträge im Verzeichnis nicht auflisten (also müssen Sie ihre Namen kennen). Die Ausführung ist andererseits unbedingt erforderlich - ohne dass der Benutzer die Inodes der Datei nicht lesen kann; Selbst mit W können ohne X-Dateien keine Dateien erstellt oder geändert werden. Detaillierte Erklärung unter diesem Link.

Schließlich sind die Modi im statModul verfügbar , ihre Beschreibungen sind in inode (7) man .

Beispielcode zur Überprüfung:

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
Todor Minakov
quelle