2012-06-20 315 views
31

我的應用程序中,我有以下請求: 1.有一個線程會定期記錄文件中的某些日誌。日誌文件將在一定的時間間隔內滾動。保持日誌文件小。 2.還有另一個線程也會定期處理這些日誌文件。例如:將日誌文件移到其他位置,解析日誌的內容以生成一些日誌報告。在Python中檢查文件是否未打開(未被其他進程使用)

但是,有一個條件是第二個線程無法處理用於記錄日誌的日誌文件。在代碼方面,僞同類者象下面這樣:

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

那麼,怎樣檢查是一個文件已經打開,或者被其他進程? 我在網上做了一些研究。並有一定的效果:

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

我嘗試這樣的代碼,但它不工作,不管我用「R +」或「+」標誌

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

這段代碼可以工作,但它無法達到我的要求,因爲我不想刪除文件以檢查它是否處於打開狀態。

+0

您是否嘗試將'os.remove'更改爲最後'try'塊內的'ProcessLogFile'?也許調整錯誤編號:有'EBUSY'和[其他](http://docs.python.org/library/errno.html)嘗試。 –

+1

你可能想讀這個問題http://stackoverflow.com/questions/2023608/check-what-files-are-open-in-python,特別是這個http://stackoverflow.com/a/7142094/546873回答 – Nicoretti

+0

如何在Windows平臺上做類似的事情來列出打開的文件。 – zengwke

回答

26

試圖找出文件是否被另一個進程使用的問題是競爭條件的可能性。您可以檢查一個文件,確定它沒有被使用,然後在打開它之前,另一個進程(或線程)跳入並抓取它(甚至刪除它)。

好吧,讓我們假設你決定忍受這種可能性,並希望它不會發生。檢查其他進程使用的文件取決於操作系統。

在Linux上很簡單,只需遍歷/ proc中的PID即可。這裏是一個生成器遍歷文件在使用特定的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) 

在Windows上,沒有這麼簡單,這些API沒有公佈。還有就是可以使用的Sysinternals工具(handle.exe),但我推薦的PyPI模塊psutil,這是便攜式(即,它運行在Linux上的歡迎,並可能對其他操作系統):

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

感謝您的回答。但是,對不起,我不能嘗試安裝psutil包。由於應用框架的侷限性。我不能包含其他第三方軟件包。有什麼辦法可以通過使用純python2.4來做到這一點? – zengwke

+0

不使用標準庫,沒有。另一種方法是自己編寫C語言或使用ctypes - 很多工作 – cdarke

+1

非常好,但在你的Linux例子中,我建議使用errno.ENOENT而不是值爲2. – kmarsh

3

你可以使用inotify觀察文件系統中的活動。您可以觀看文件關閉事件,表明發生翻車事件。您還應該在文件大小上添加附加條件。確保從第二個線程過濾掉文件關閉事件。

1

而是使用os.remove(),您可以使用以下解決方法在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

這裏比賽條件。如果用戶在第一次重命名後中斷程序(ctrl-c),則文件名將不會被恢復,用戶將不會意識到這種情況。至少應該將兩個重命名操作組合在一起。應該打印出來。這最大限度地減少了危險窗口。 os.rename(---); os.rename(---);打印「Access ---」您還應該捕獲KeyboardInterrupt和SystemExit異常,以便在應用程序退出前嘗試恢復文件名。 –

+1

或只是最後使用 – user25064

+0

這是一個非常愚蠢的解決方案!可執行文件和dll可以在Windows中重新命名... –

14

我喜歡丹尼爾的答案,但我意識到,這是更安全,更簡單的文件已經重命名爲它命名具有。這解決了在評論中提出的問題,他的答案。我只是在評論中這樣說,但我沒有這些觀點。下面的代碼:

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

我很確定這不適用於非Windows操作系統(我的Linux系統很容易讓我重命名我在另一個進程中打開的數據庫文件)。 –

6

您可以檢查文件是否具有使用下一個函數(記住的完整路徑傳遞給文件)上有一個手柄:

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

真不錯!謝謝 – ZHAJOR

0

我知道我晚了,但我也有這個問題,我用lsof命令來解決它(我認爲是從上述方法新)。使用lsof我們基本上可以檢查使用這個特定文件的進程。 這是我如何做的:

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 

只寫你的日誌處理代碼中除了一部分,你是好去。

相關問題