Menentukan Apakah Direktori Dapat Ditulisi


101

Apa cara terbaik dengan Python untuk menentukan apakah direktori dapat ditulisi untuk pengguna yang menjalankan skrip? Karena ini kemungkinan akan melibatkan penggunaan modul os, saya harus menyebutkan bahwa saya menjalankannya di bawah lingkungan * nix.

Jawaban:


185

Meskipun yang disarankan Christophe adalah solusi yang lebih Pythonic, modul os memang memiliki fungsi os.access untuk memeriksa akses:

os.access('/path/to/folder', os.W_OK) # W_OK untuk menulis, R_OK untuk membaca, dll.


4
Bergantung pada situasinya, "lebih mudah untuk meminta maaf" bukanlah cara terbaik, bahkan dengan Python. Kadang-kadang disarankan untuk "meminta izin" seperti dengan metode os.access () yang disebutkan, misalnya ketika probabilitas harus menangkap kesalahan tinggi.
mjv

53
Menguji direktori hanya untuk bit tulis tidak cukup jika Anda ingin menulis file ke direktori. Anda juga perlu menguji bit eksekusi jika ingin menulis ke direktori. os.access ('/ path / to / folder', os.W_OK | os.X_OK) Dengan os.W_OK sendiri, Anda hanya dapat menghapus direktori (dan hanya jika direktori itu kosong)
fthinker

4
Gotcha lain dari os.access()itu cek menggunakan nyata UID dan GID, bukan efektif yang. Hal ini dapat menyebabkan keanehan di lingkungan SUID / SGID. ('tetapi skrip menjalankan setuid root, mengapa tidak bisa menulis ke file?')
Alexios

5
Mungkin sebuah program hanya ingin tahu tanpa harus benar-benar menulis. Mungkin hanya ingin mengubah tampilan dan / atau perilaku GUI sesuai dengan propertinya. Dalam hal ini saya tidak akan menganggap itu pythonic untuk menulis dan menghapus file hanya sebagai ujian.
Bachsau

1
Baru saja diuji pada jaringan berbagi Windows. os.access(dirpath, os.W_OK | os.X_OK)mengembalikan True meskipun saya tidak memiliki akses tulis.
iamanigeeit

69

Mungkin tampak aneh untuk menyarankan ini, tetapi idiom Python yang umum adalah

Lebih mudah meminta maaf daripada izin

Mengikuti idiom itu, orang mungkin berkata:

Coba tulis ke direktori yang dimaksud, dan tangkap kesalahannya jika Anda tidak memiliki izin untuk melakukannya.


5
+1 Python atau bukan, ini benar-benar cara paling andal untuk menguji akses.
John Knoeller

5
Ini juga menangani kesalahan lain yang dapat terjadi saat menulis ke disk - misalnya tidak ada ruang disk yang tersisa. Itulah kekuatan mencoba .. Anda tidak perlu mengingat semua yang bisa salah ;-)
Jochen Ritzel

4
Terima kasih teman-teman. Memutuskan untuk menggunakan os.access karena kecepatan merupakan faktor penting dalam apa yang saya lakukan di sini meskipun saya pasti bisa memahami manfaat dalam "lebih mudah meminta maaf daripada izin." ;)
illuminatedtiger

4
Itu IDIO yang hebat ... m - terutama jika digabungkan dengan idiom lain except: pass- dengan cara ini Anda selalu bisa optimis dan memikirkan diri sendiri. / sarkasme off. Sekarang mengapa saya ingin, misalnya mencoba menulis sesuatu ke setiap direktori di sistem file saya, untuk membuat daftar lokasi yang dapat ditulis?
Tomasz Gandor

4
Mungkin sebuah program hanya ingin tahu tanpa harus benar-benar menulis. Mungkin hanya ingin mengubah tampilan dan / atau perilaku GUI sesuai dengan propertinya. Dalam hal ini saya tidak akan menganggap itu pythonic untuk menulis dan menghapus file hanya sebagai ujian.
Bachsau

19

Solusi saya menggunakan 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

Pembaruan: Setelah menguji kode lagi pada Windows saya melihat bahwa memang ada masalah saat menggunakan tempfile di sana, lihat issue22107: kesalahan tafsir modul tempfile akses ditolak pada Windows . Dalam kasus direktori yang tidak dapat ditulis, kode macet selama beberapa detik dan akhirnya melempar IOError: [Errno 17] No usable temporary file name found. Mungkin ini yang diamati oleh user2171842? Sayangnya masalah tersebut belum teratasi untuk saat ini, jadi untuk menanganinya, kesalahan juga perlu diperhatikan:

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

Penundaan tentu saja masih ada dalam kasus-kasus ini.


1
Menurut saya yang menggunakan tempfile adalah yang lebih bersih karena pasti tidak meninggalkan residu.
belalang

3
metode ini tidak berfungsi menggunakan tempfile. ini hanya berfungsi jika tidak ada OSErrorartinya ia memiliki izin untuk menulis / menghapus. selain itu tidak akan terjadi return Falsekarena tidak ada kesalahan yang dikembalikan, dan skrip tidak akan terus dijalankan atau keluar. tidak ada yang dikembalikan. itu hanya terjebak di garis itu. Namun, membuat file non-sementara seperti jawaban khattam berfungsi baik ketika izin diizinkan atau ditolak. Tolong?

10

Tersandung di utas ini mencari contoh untuk seseorang. Hasil pertama di Google, selamat!

Orang-orang berbicara tentang cara Pythonic melakukannya di utas ini, tetapi tidak ada contoh kode sederhana? Ini dia, untuk siapa pun yang tersandung:

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

Ini mencoba untuk membuka filehandle untuk menulis, dan keluar dengan kesalahan jika file yang ditentukan tidak dapat ditulis ke: Ini jauh lebih mudah dibaca, dan merupakan cara yang jauh lebih baik untuk melakukannya daripada melakukan prechecks pada jalur file atau direktori , karena menghindari kondisi balapan; kasus di mana file menjadi tidak dapat ditulis antara saat Anda menjalankan pemeriksaan awal, dan saat Anda benar-benar mencoba untuk menulis ke file.


1
Ini berlaku untuk file, bukan direktori yang diminta OP. Anda dapat memiliki file dalam sebuah direktori dan direktori tersebut tidak dapat ditulis tetapi file itu sendiri ada, jika file tersebut sudah ada. Ini mungkin penting dalam administrasi sistem di mana Anda misalnya membuat file log yang Anda inginkan sudah ada tetapi tidak ingin orang menggunakan direktori log untuk ruang sementara.
Mike S

... dan sebenarnya saya menolaknya, yang menurut saya sekarang adalah kesalahan. Ada masalah, seperti yang dikatakan Rohaq, dengan kondisi ras. Ada masalah lain pada berbagai platform di mana Anda dapat menguji direktori, dan itu terlihat dapat ditulis, tetapi sebenarnya tidak. Melakukan pemeriksaan yang dapat ditulis direktori lintas platform lebih sulit daripada yang terlihat. Jadi selama Anda mengetahui masalahnya, ini mungkin teknik yang bagus. Saya melihatnya dari perspektif yang terlalu UNIX-y, yang merupakan kesalahan saya. Seseorang mengedit jawaban ini sehingga saya bisa menghapus -1 saya.
Mike S

Saya telah mengeditnya, jika Anda ingin menghapus -1 :) Dan ya, pemeriksaan direktori lintas platform bisa menjadi lebih rumit, tetapi umumnya Anda ingin membuat / menulis ke file di direktori itu - dalam hal ini contoh yang saya berikan masih harus diterapkan. Jika beberapa masalah terkait izin direktori muncul, IOError harus tetap muncul saat mencoba membuka filehandle.
Rohaq

Saya menghapus suara negatif saya. Maaf tentang itu, dan terima kasih atas kontribusi Anda.
Mike S

Jangan khawatir, orang-orang yang menanyakan jawaban selalu disambut!
Rohaq

9

Jika Anda hanya peduli tentang file perm, os.access(path, os.W_OK)harus melakukan apa yang Anda minta. Jika Anda justru ingin mengetahui apakah Anda dapat menulis ke direktori, open()file pengujian untuk ditulis (seharusnya tidak ada sebelumnya), tangkap dan periksa apa saja IOError, dan bersihkan file pengujian setelahnya.

Secara lebih umum, untuk menghindari serangan TOCTOU (hanya masalah jika skrip Anda berjalan dengan hak istimewa yang lebih tinggi - suid atau cgi atau lebih), Anda tidak boleh benar-benar mempercayai pengujian yang akan dilakukan sebelumnya ini, tetapi jatuhkan privasi, lakukan open(), dan harapkan itu IOError.


7

Periksa bit mode:

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
Solusi ini hanya Unix.
Björn Lindqvist

4

Ini adalah sesuatu yang saya buat berdasarkan jawaban 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")

info lebih lanjut tentang akses dapat ditemukan di sini


2
Ini pada dasarnya adalah salinan dari jawaban Max Shawabkeh dengan sedikit pembungkus di sekitarnya. Membuatnya menjadi copy paste cepat tetapi ide yang lebih baik adalah menambahkannya ke postingan asli Max.
Jorrick Sleijster

1

Saya mengalami kebutuhan yang sama ini sambil menambahkan argumen melalui argparse. Bawaan type=FileType('w')tidak akan berfungsi untuk saya karena saya sedang mencari direktori. Saya akhirnya menulis metode saya sendiri untuk memecahkan masalah saya. Inilah hasil dengan potongan 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()

Hasilnya sebagai berikut:

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

Saya kembali dan menambahkan print opts.dir sampai akhir, dan semuanya tampak berfungsi seperti yang diinginkan.


0

Jika Anda perlu memeriksa izin pengguna lain (ya, saya menyadari ini bertentangan dengan pertanyaan, tetapi mungkin berguna untuk seseorang), Anda dapat melakukannya melalui pwdmodul, dan bit mode direktori.

Penafian - tidak berfungsi di Windows, karena tidak menggunakan model izin POSIX (dan pwdmodul tidak tersedia di sana), misalnya - solusi hanya untuk sistem * nix.

Perhatikan bahwa direktori harus memiliki semua 3 bit set - Baca, Tulis dan eXecute.
Ok, R bukanlah suatu keharusan mutlak, tetapi tanpa itu Anda tidak dapat mencantumkan entri dalam direktori (jadi Anda harus mengetahui namanya). Eksekusi di sisi lain mutlak diperlukan - tanpa pengguna tidak dapat membaca inode file; jadi meskipun memiliki W, tanpa X file tidak dapat dibuat atau dimodifikasi. Penjelasan lebih detail ada di link ini.

Akhirnya, mode tersedia di statmodul, deskripsinya ada di inode (7) man .

Kode contoh cara memeriksa:

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.