2012-06-20 13 views
31

Moja aplikacja, mam następujące żądania: 1. Nie ma jednego wątku będzie regularnie nagrywać niektóre dzienniki w pliku. Plik dziennika będzie rolować w określonych odstępach czasu. do przechowywania małych plików dziennika. 2. Nie ma innego wątku również będzie regularnie przetwarzać te pliki dziennika. Przykład: Przenieś pliki dziennika do innego miejsca, przeanalizuj zawartość dziennika, aby wygenerować niektóre raporty dziennika.Sprawdź, czy plik nie jest otwarty (nieużywany w innym procesie) w Pythonie

Ale istnieje warunek, że drugi wątek nie może przetworzyć pliku dziennika, który jest używany do rejestrowania dziennika. w stronie kodowej, że podobieństw pseudokod jak poniżej:

#code in second thread to process the log files 
for logFile in os.listdir(logFolder): 
    if not file_is_open(logFile) or file_is_use(logFile): 
      ProcessLogFile(logFile) # move log file to other place, and generate log report.... 

Tak, jak mogę sprawdzić to plik jest już otwarty i jest używany przez inny proces? Zrobiłem trochę badań w Internecie. I pewne wyniki:

try: 
    myfile = open(filename, "r+") # or "a+", whatever you need 
except IOError: 
    print "Could not open file! Please close Excel!" 

próbowałem tego kodu, ale to nie działa, nie ważne używam "r +" lub "+" flag

try: 
    os.remove(filename) # try to remove it directly 
except OSError as e: 
    if e.errno == errno.ENOENT: # file doesn't exist 
     break 

Kod ten może pracować, ale to nie mogę osiągnąć mojego żądania, ponieważ nie chcę usuwać pliku, aby sprawdzić, czy jest otwarty.

+0

Czy próbowałeś zmienić 'os.remove' na' ProcessLogFile' wewnątrz ostatniego bloku 'try'? Może poprawić numer błędu: są "EBUSY" i [inne] (http://docs.python.org/library/errno.html), aby spróbować. –

+1

Możesz chcieć przeczytać to pytanie: http://stackoverflow.com/questions/2023608/check-what-files-are-open-in-python, a szczególnie ta http://stackoverflow.com/a/7142094/546873 odpowiedź – Nicoretti

+0

Jak zrobić podobne rzeczy na platformie Windows, aby wyświetlić listę otwartych plików. – zengwke

Odpowiedz

26

Problem z próbą sprawdzenia, czy plik jest używany przez inny proces, to możliwość wystąpienia wyścigu. Możesz sprawdzić plik, zdecydować, że nie jest on używany, a tuż przed otwarciem innego procesu (lub wątku) przeskakuje i chwyta (lub nawet usuwa).

OK, załóżmy, że zdecydujesz się żyć z tą możliwością i mieć nadzieję, że to nie nastąpi. Sprawdzanie plików używanych przez inne procesy zależy od systemu operacyjnego.

W przypadku Linuksa jest to dość łatwe, wystarczy przejrzeć PID w/proc. Tutaj jest generator, który wykonuje iteracje nad pliki używane przez określony PID:

def iterate_fds(pid): 
    dir = '/proc/'+str(pid)+'/fd' 
    if not os.access(dir,os.R_OK|os.X_OK): return 

    for fds in os.listdir(dir): 
     for fd in fds: 
      full_name = os.path.join(dir, fd) 
      try: 
       file = os.readlink(full_name) 
       if file == '/dev/null' or \ 
        re.match(r'pipe:\[\d+\]',file) or \ 
        re.match(r'socket:\[\d+\]',file): 
        file = None 
      except OSError as err: 
       if err.errno == 2:  
        file = None 
       else: 
        raise(err) 

      yield (fd,file) 

W systemie Windows nie jest tak proste, że API nie są publikowane. Jest to narzędzie Sysinternals (handle.exe), które mogą być używane, ale polecam moduł PyPI psutil, który jest przenośny (tzn działa na Linux, jak również i prawdopodobnie na innym systemie operacyjnym):

import psutil 

for proc in psutil.process_iter(): 
    try: 
     flist = proc.get_open_files() 
     if flist: 
      print(proc.pid,proc.name) 
      for nt in flist: 
       print("\t",nt.path) 

    # This catches a race condition where a process ends 
    # before we can examine its files  
    except psutil.NoSuchProcess as err: 
     print("****",err) 
+0

Dziękuję za odpowiedzi. Ale przykro mi, nie mogę spróbować zainstalować pakietu psutil. Od ograniczenia ramy aplikacji. Nie mogę uwzględnić innych pakietów dla osób trzecich. Czy jest jakiś sposób to zrobić, używając czystego Pythona2.4? – zengwke

+0

Nie używając biblioteki standardowej, nie. Inną alternatywą jest napisanie go w C lub użycie ctypów - dużo pracy – cdarke

+1

Bardzo dobrze, ale w twoim przykładzie Linuksa sugeruję użycie errno.ENOENT zamiast wartości 2. – kmarsh

3

Można Użyj inotify, aby obejrzeć aktywność w systemie plików. Możesz oglądać zdarzenia związane z zamknięciem pliku, wskazując, że nastąpiło wywrócenie. Powinieneś również dodać dodatkowy warunek w rozmiarze pliku. Upewnij się, że odfiltrowujesz zdarzenia zamknięcia pliku z drugiego wątku.

1

Zamiast na wykorzystaniu os.remove() można użyć następującego obejścia Windows:

import os 

file = "D:\\temp\\test.pdf" 
if os.path.exists(file): 
    try: 
     os.rename(file,file+"_") 
     print "Access on file \"" + str(file) +"\" is available!" 
     os.rename(file+"_",file) 
    except OSError as e: 
     message = "Access-error on file \"" + str(file) + "\"!!! \n" + str(e) 
     print message 
+3

Stan wyścigu tutaj. Jeśli użytkownik przerywa program (ctrl-c) po pierwszej zmianie nazwy, nazwa pliku nie zostanie przywrócona i użytkownik nie będzie tego świadomy. Jako minimum należy sparować dwie operacje zmiany nazwy razem. Wydruk powinien iść dalej. Minimalizuje to okno zagrożenia. os.rename (---); os.rename (---); print "Access ---" Powinieneś także wychwycić wyjątki KeyboardInterrupt i SystemExit, abyś mógł spróbować przywrócić nazwę pliku przed zamknięciem aplikacji. –

+1

lub po prostu użyj w końcu – user25064

+0

To jest bardzo głupie rozwiązanie! pliki wykonywalne i biblioteki DLL mogą mieć zmienioną nazwę w systemie Windows, gdy są otwarte ... –

14

Lubię odpowiedź Daniela, ale zdałem sobie sprawę, że jest to bezpieczniejsze i prostsze, aby zmienić nazwę pliku do nazwy już ma. To rozwiązuje problemy poruszone w odpowiedziach. Powiedziałbym to po prostu w komentarzu, ale nie mam punktów.Oto kod:

import os 

f = 'C:/test.xlsx' 
if os.path.exists(f): 
    try: 
     os.rename(f, f) 
     print 'Access on file "' + f +'" is available!' 
    except OSError as e: 
     print 'Access-error on file "' + f + '"! \n' + str(e) 
+1

Jestem prawie pewien, że to nie zadziała na systemach innych niż Windows (mój system Linux pozwolił mi zmienić nazwę pliku bazy danych, który miałem otwarty w innym procesie). –

6

Można sprawdzić, czy plik ma uchwyt na nim przy użyciu następną funkcję (pamiętaj, aby przejść pełną ścieżkę dostępu do tego pliku):

import psutil 

def has_handle(fpath): 
    for proc in psutil.process_iter(): 
     try: 
      for item in proc.open_files(): 
       if fpath == item.path: 
        return True 
     except Exception: 
      pass 

    return False 
+0

Naprawdę ładne! Dzięki – ZHAJOR

0

wiem, że jestem spóźniłem się na imprezę, ale miałem też ten problem i użyłem polecenia lsof, aby go rozwiązać (co według mnie jest nowe z wyżej wymienionych podejść). Dzięki lsof możemy zasadniczo sprawdzić procesy, które używają tego konkretnego pliku. Oto jak to zrobiłem:

from subprocess import check_output,Popen, PIPE 
try: 
    lsout=Popen(['lsof',filename],stdout=PIPE, shell=False) 
    check_output(["grep",filename], stdin=lsout.stdout, shell=False) 
except: 
    #check_output will throw an exception here if it won't find any process using that file 

tylko napisać kod przetwarzanie dziennika w wyłączeniem części i jesteś dobry, aby przejść.

Powiązane problemy