Bir Dizinin Yazılabilir Olup Olmadığını Belirleme


101

Python'da komut dosyasını çalıştıran kullanıcı için bir dizinin yazılabilir olup olmadığını belirlemenin en iyi yolu nedir? Bu muhtemelen os modülünü kullanmayı içereceğinden, onu bir * nix ortamında çalıştırdığımı söylemeliyim.

Yanıtlar:


185

Christophe'un önerdiği daha Pythonic bir çözüm olsa da, işletim sistemi modülü, erişimi kontrol etmek için os.access işlevine sahiptir:

os.access('/path/to/folder', os.W_OK) # W_OK yazmak için, R_OK okumak için vb.


4
Duruma bağlı olarak, "af dilemek daha kolay", Python'da bile en iyi yol değildir. Bazen bahsedilen os.access () yönteminde olduğu gibi, örneğin bir hatayı yakalama olasılığı yüksek olduğunda "izin istemek" önerilir.
mjv

53
Dizine dosya yazmak istiyorsanız, bir dizini yalnızca yazma biti için test etmek yeterli değildir. Dizine yazmak istiyorsanız, yürütme bitini de test etmeniz gerekecektir. os.access ('/ yol / / klasör', os.W_OK | os.X_OK) os.W_OK ile yalnızca dizini silebilirsiniz (ve yalnızca bu dizin boşsa)
fthinker

4
Başka bir bilgi de, etkili olanları değil, gerçek UID ve GID'yi os.access()kullanarak kontrol etmektir . Bu SUID / SGID ortamlarında tuhaflığa neden olabilir. ('ama betik setuid root çalıştırıyor, neden dosyaya yazamıyor?')
Alexios

5
Belki bir program gerçekten yazmaya ihtiyaç duymadan bilmek istiyordur. Özelliğe göre bir GUI'nin görünümünü ve / veya davranışını değiştirmek isteyebilir. Bu durumda, bir dosyayı sadece bir test olarak yazıp silmenin pitonik olduğunu düşünmem.
Bachsau

1
Bir Windows ağ paylaşımında test edildi. os.access(dirpath, os.W_OK | os.X_OK)Yazma erişimim olmasa bile True döndürüyor.
iamanigeeit

69

Bunu önermek tuhaf görünebilir, ancak yaygın bir Python deyimi

Af dilemek izin istemekten daha kolaydır

Bu deyimin ardından şöyle diyebiliriz:

Söz konusu dizine yazmayı deneyin ve izniniz yoksa hatayı yakalayın.


6
+1 Python ya da değil, bu gerçekten erişimi test etmenin en güvenilir yoludur.
John Knoeller

5
Bu aynı zamanda diske yazarken meydana gelebilecek diğer hataları da giderir - örneğin disk alanı kalmaz.
Denemenin

4
Teşekkürler beyler. Os.access ile devam etmeye karar verdim, çünkü burada yaptığım şeyde hız önemli bir faktör olsa da, "Af dilemek izin istemekten daha kolaydır" ifadesinin esaslarını kesinlikle anlayabiliyorum. ;)
illuminatedtiger

4
Bu harika bir IDIO ... m - özellikle başka bir deyimle birleştiğinde except: pass- bu şekilde her zaman iyimser olabilir ve kendinizi çok iyi düşünebilirsiniz. / alaycı. Şimdi neden yazılabilir konumların bir listesini oluşturmak için dosya sistemimdeki her dizine bir şeyler yazmaya çalışayım?
Tomasz Gandor

4
Belki bir program gerçekten yazmaya ihtiyaç duymadan bilmek istiyordur. Özelliğe göre bir GUI'nin görünümünü ve / veya davranışını değiştirmek isteyebilir. Bu durumda, bir dosyayı sadece bir test olarak yazıp silmenin pitonik olduğunu düşünmem.
Bachsau

19

tempfileModülü kullanan çözümüm :

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

Güncelleme: Kodu Windows'ta tekrar test ettikten sonra, orada geçici dosyayı kullanırken gerçekten bir sorun olduğunu görüyorum, bkz. İssue22107: tempfile modülü, Windows'ta erişim reddedildi hatasını yanlış yorumluyor . Yazılamayan bir dizin olması durumunda, kod birkaç saniye askıda kalır ve sonunda bir IOError: [Errno 17] No usable temporary file name found. Belki de user2171842'nin gözlemlediği budur? Maalesef sorun şimdilik çözülmedi, bu nedenle bunun üstesinden gelmek için hatanın da yakalanması gerekiyor:

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

Bu durumlarda gecikme tabii ki hala mevcuttur.


1
Bence tempfile kullanan kişi temizleyici çünkü kesinlikle kalıntı bırakmıyor.
çekirge

3
bu yöntem kullanılarak çalışmıyor tempfile. sadece OSErroryazma / silme iznine sahip olmadığı zaman çalışır . aksi halde return Falsehata döndürülmediğinden ve betik çalışmaya veya çıkmaya devam etmeyeceğinden dönmez. hiçbir şey iade edilmez. sadece o çizgide kaldı. ancak, khattam'ın yanıtı gibi geçici olmayan bir dosya oluşturmak hem izin verildiğinde hem de reddedildiğinde işe yarar. Yardım?

10

Birisi için örnekler ararken bu ileti dizisine rastladım. Google'daki ilk sonuç, tebrikler!

İnsanlar bu yazıda bunu yapmanın Pythonic yolu hakkında konuşuyorlar, ancak basit kod örnekleri yok mu? Tökezleyen herkes için işte buyrun:

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

Bu, yazma için bir dosya tanıtıcısı açmaya çalışır ve belirtilen dosya yazılamazsa bir hatayla çıkar: Bu, okunması çok daha kolaydır ve dosya yolunda veya dizinde ön kontroller yapmaktan çok daha iyi bir yöntemdir. yarış koşullarından kaçındığı için; ön denetimi çalıştırdığınız zaman ile gerçekten dosyaya yazmaya çalıştığınız zaman arasında dosyanın yazılamaz hale geldiği durumlar.


1
Bu, OP'nin sorduğu dizin değil, dosya için geçerlidir. Bir dizinde bir dosyanız olabilir ve dizin yazılabilir olmayabilir, ancak dosya zaten mevcutsa dosyanın kendisi yazılabilir. Bu, örneğin zaten var olmasını istediğiniz ancak insanların geçici alan için bir günlük dizini kullanmasını istemediğiniz günlük dosyaları oluşturduğunuz sistem yönetiminde önemli olabilir.
Mike S

... ve aslında onu reddettim, ki şimdi bunun bir hata olduğunu düşünüyorum. Rohaq'ın da bahsettiği gibi, yarış koşullarıyla ilgili sorunlar var. Dizini test edebileceğiniz çeşitli platformlarda başka sorunlar da var ve yazılabilir görünüyor, ancak aslında değil. Platformlar arası dizine yazılabilir kontroller yapmak göründüğünden daha zordur. Sorunların farkında olduğunuz sürece, bu iyi bir teknik olabilir. Ben ona fazlasıyla UNIX-y perspektifinden bakıyordum ki bu benim hatam. Biri bu yanıtı düzenlesin, böylece -1'imi kaldırabilirim.
Mike S

-1'i kaldırmak istemeniz durumunda onu düzenledim :) Ve evet, platformlar arası dizin kontrolleri daha karmaşık hale gelebilir, ancak genellikle o dizinde bir dosya oluşturmak / yazmak istiyorsunuz - bu durumda Verdiğim örnek hala geçerli olmalı. Dizin izinleriyle ilgili bir sorun ortaya çıkarsa, dosya tanıtıcısını açmaya çalışırken yine de bir IOError atması gerekir.
Rohaq

Olumsuz oyumu kaldırdım. Bunun için üzgünüm ve katkınız için teşekkürler.
Mike S

Endişelenmeyin, cevapları sorgulayan insanlar her zaman memnuniyetle karşılanır!
Rohaq

9

Yalnızca dosya izinleriyle ilgileniyorsanız, os.access(path, os.W_OK)istediğinizi yapmalısınız. Bunun yerine dizine yazıp yazamayacağınızı öğrenmek istiyorsanız , yazmak için open()bir test dosyası (önceden mevcut olmamalıdır), herhangi birini yakalayıp inceleyin IOErrorve daha sonra test dosyasını temizleyin.

Daha genel olarak, TOCTOU saldırılarından kaçınmak için (yalnızca betiğiniz yükseltilmiş ayrıcalıklarla çalışıyorsa bir sorundur - suid veya cgi veya benzeri), bu vaktinden önce yapılan testlere gerçekten güvenmemeli, ayrıcalıkları bırakmalı, yapmalı open()ve beklemelisiniz IOError.


7

Mod bitlerini kontrol edin:

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
Bu çözüm yalnızca Unix'tir.
Björn Lindqvist

4

İşte ChristopheD'nin cevabına göre oluşturduğum bir şey:

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

erişim hakkında daha fazla bilgiyi burada bulabilirsiniz


2
Bu, temelde Max Shawabkeh'in etrafında küçük bir paket bulunan cevabının bir kopyası. Hızlı bir kopyalayıp yapıştırma yapar, ancak daha iyi bir fikir, onu Max'in orijinal gönderisine eklemek olacaktır.
Jorrick Sleijster

1

Argparse aracılığıyla bir argüman eklerken de aynı ihtiyaca rastladım. type=FileType('w')Bir dizin aradığım için yerleşik benim için işe yaramazdı. Sorunumu çözmek için kendi yöntemimi yazdım. İşte argparse snippet'inin sonucu.

#! /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()

Bu, aşağıdakilerle sonuçlanır:

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

Geri döndüm ve sonuna print opts.dir ekledim ve her şey istendiği gibi çalışıyor gibi görünüyor.


0

Başka bir kullanıcının iznini kontrol etmeniz gerekiyorsa (evet, bunun soruyla çeliştiğini anlıyorum, ancak birisi için kullanışlı olabilir), bunu şu adresten yapabilirsiniz:pwd modül ve dizinin mod bitleri aracılığıyla yapabilirsiniz.

Sorumluluk Reddi - POSIX izin modelini kullanmadığı için (ve pwdmodül orada bulunmadığından) Windows'ta çalışmaz , örneğin - sadece * nix sistemleri için çözüm.

Bir dizinin 3 bitin de ayarlanması gerektiğini unutmayın - Okuma, Yazma ve eXecute.
Tamam, R mutlak bir zorunluluk değildir, ancak bu olmadan dizindeki girişleri listeleyemezsiniz (bu yüzden isimlerini bilmeniz gerekir). Öte yandan, yürütme kesinlikle gereklidir - kullanıcı dosyanın düğümlerini okuyamaz; bu nedenle W'ye sahip olsanız bile, X dosyaları oluşturulamaz veya değiştirilemez. Bu bağlantıda daha ayrıntılı açıklama.

Son olarak, modlar statmodülde mevcuttur , açıklamaları inode (7) man içindedir .

Örnek kod nasıl kontrol edilir:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.