Jak obejrzeć plik pod kątem zmian?

323

Mam plik dziennika zapisywany przez inny proces, który chcę obserwować pod kątem zmian. Za każdym razem, gdy nastąpi zmiana, chciałbym wczytać nowe dane, aby je trochę przetworzyć.

Jak najlepiej to zrobić? Miałem nadzieję, że będzie jakiś haczyk z biblioteki PyWin32. Znalazłem tę win32file.FindNextChangeNotificationfunkcję, ale nie mam pojęcia, jak poprosić ją o obejrzenie określonego pliku.

Jeśli ktoś zrobiłby coś takiego, byłbym bardzo wdzięczny za to, jak ...

[Edytuj] Powinienem wspomnieć, że szukałem rozwiązania, które nie wymaga odpytywania.

[Edytuj] Przekleństwa! Wygląda na to, że to nie działa na zmapowanym dysku sieciowym. Domyślam się, że Windows nie „słyszy” żadnych aktualizacji pliku, tak jak na dysku lokalnym.

Jon Cage
źródło
1
w Linuksie można do tego wykorzystać wywołania stracemonitorującewrite
test30
Odpowiedź @ simao korzysta z języka python-watchdog. Python-Watchdog ma świetną dokumentację -> tutaj jest link do dokumentacji [„QuickStart”], która zawiera minimalny przykład kodu obserwujący bieżący katalog roboczy.
Trevor Boyd Smith

Odpowiedzi:

79

Czy przeglądałeś już dokumentację dostępną na stronie http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.html ? Jeśli potrzebujesz go tylko do pracy w systemie Windows, drugi przykład wydaje się być dokładnie tym, czego chcesz (jeśli zamienisz ścieżkę katalogu na plik z plikiem, który chcesz oglądać).

W przeciwnym razie odpytywanie będzie prawdopodobnie jedyną opcją niezależną od platformy.

Uwaga: nie wypróbowałem żadnego z tych rozwiązań.

Horst Gutmann
źródło
5
Ta odpowiedź jest specyficzna dla systemu Windows, ale wygląda na to, że opublikowano tutaj również międzyplatformowe rozwiązania tego problemu.
Anderson Green
Czy istnieje test porównawczy, jeśli proces ten przebiega wolniej niż implementacja w języku ojczystym, takim jak c ++?
user1767754
Lepiej wstawić odpowiednią treść z cytowanych źródeł, ponieważ mogą one stać się nieaktualne.
Trilarion
2
(1.) na końcu tej odpowiedzi znajduje się wyraźne zastrzeżenie ... „Nie próbowałem żadnego z tych rozwiązań”. (2.) ta odpowiedź jest mniej więcej odpowiedzią „tylko link” (3.) odpowiedź wspomina o „odpytywaniu”, ale nie dodaje nic pomocnego po tym ... gdzie jak @ Deestana zawiera dobre informacje na temat odpytywania
Trevor Boyd Smith
283

Czy próbowałeś użyć Watchdog ?

Biblioteka API i narzędzia powłoki Pythona do monitorowania zdarzeń w systemie plików.

Łatwe monitorowanie katalogów

  • Wieloplatformowy interfejs API.
  • Narzędzie powłoki do uruchamiania poleceń w odpowiedzi na zmiany katalogu.

Zacznij szybko dzięki prostemu przykładowi w Quickstart ...

simao
źródło
56
Instalowalny z easy_install? Czek. Darmowa licencja? Sprawdzić . Rozwiązuje problem na dużych platformach? Sprawdzić . Popieram tę odpowiedź. Tylko uwaga: przykład na stronie projektu nie działa od razu po wyjęciu z pudełka. Zamiast tego użyj tego na ich githubie .
Inaimathi
6
Używamy watchdoga. Możemy przejść do QFileSystemWatcher. Tylko uczciwe ostrzeżenie - strażnik jest dobry, ale daleki od ideału na wszystkich platformach (w tej chwili). Każdy system operacyjny ma swoje osobliwości. Tak więc, o ile nie jesteś zaangażowany w doskonalenie go, będziesz wyciągał włosy. Jeśli chcesz tylko obejrzeć 10 plików, sonduję. Buforowanie dysku systemu operacyjnego jest bardzo dojrzałe, a Watchdog i tak wymaga API sondowania. Służy głównie do oglądania ogromnych struktur folderów IMHO.
SilentSteel
3
Mój jedyny problem z organem nadzorczym polega na tym, że ma wiele zależności. Oczywiście mniej niż PyQt, ale nie działa i wydaje się, że jest to minimalna najlepsza praktyka, która wykonuje jedno zadanie i robi to dobrze.
AndreasT
1
Czy @denfromufa jest tutaj poprawne? Czy watchdog naprawdę blokuje pliki, więc nie można ich jednocześnie edytować, aby watchdog je oglądał? Nie mogę w to uwierzyć, byłoby to całkowicie bezużyteczne.
Michel Müller,
1
@ MichelMüller Właśnie sprawdziłem ten przykład (patrz link poniżej) i działa! Nie jestem pewien, co było wcześniej, ale ta odpowiedź nie zawiera żadnego przykładu. stackoverflow.com/a/18599427/2230844
denfromufa
92

Jeśli odpytywanie jest dla Ciebie wystarczające, po prostu obserwuję, czy zmieniają się statystyki pliku „zmodyfikowanego czasu”. Aby to przeczytać:

os.stat(filename).st_mtime

(Należy również pamiętać, że natywne rozwiązanie zdarzenia zmiany systemu Windows nie działa we wszystkich okolicznościach, np. Na dyskach sieciowych).

import os

class Monkey(object):
    def __init__(self):
        self._cached_stamp = 0
        self.filename = '/path/to/file'

    def ook(self):
        stamp = os.stat(self.filename).st_mtime
        if stamp != self._cached_stamp:
            self._cached_stamp = stamp
            # File has changed, so do something...
Deestan
źródło
1
Jak możesz to robić w odstępach czasu?
dopatraman
1
@dopatraman Oto, jak możesz to zrobić w odstępach `import sys import time pub = Monkey (), a True: try: time.sleep (1) pub.watch () oprócz KeyboardInterrupt: print ('\ nDone') break oprócz : print (f'Unhandled error: {sys.exc_info () [0]} ') `
Vlad Bezden
Świetne proste rozwiązanie! Dodałem czek, aby zapobiec jego zgłoszenie zmiany pliku na pierwszym biegu: if self._cached_stamp is not None.
Noumenon
50

Jeśli chcesz rozwiązania wieloplatformowego, sprawdź QFileSystemWatcher . Oto przykładowy kod (niezanieczyszczony):

from PyQt4 import QtCore

@QtCore.pyqtSlot(str)
def directory_changed(path):
    print('Directory Changed!!!')

@QtCore.pyqtSlot(str)
def file_changed(path):
    print('File Changed!!!')

fs_watcher = QtCore.QFileSystemWatcher(['/path/to/files_1', '/path/to/files_2', '/path/to/files_3'])

fs_watcher.connect(fs_watcher, QtCore.SIGNAL('directoryChanged(QString)'), directory_changed)
fs_watcher.connect(fs_watcher, QtCore.SIGNAL('fileChanged(QString)'), file_changed)
hipersayan_x
źródło
6
Myślę, że jest to prawdopodobnie najlepsza odpowiedź grupy, biorąc pod uwagę, że albo a) polegają one na obiekcie FileSystemwatcher systemu Win32 i nie mogą zostać przeniesione, lub b) odpytują o plik (co jest niekorzystne z punktu widzenia wydajności i nie będzie skalowane). Szkoda, że ​​Python nie ma wbudowanego tego narzędzia, ponieważ PyQt jest ogromną zależnością, jeśli wszystko, czego używasz, to klasa QFileSystemWatcher.
CadentOrange,
4
Podoba mi się to rozwiązanie. Chciałem zauważyć, że do działania będzie potrzebna instancja QApplication, dodałem „app = QtGui.QApplication (sys.argv)” bezpośrednio pod importem, a następnie „app.exec_ ()” po połączeniach sygnałowych.
spencewah
Właśnie testuję to na Linux-ie, widzę, że wywoływana jest metoda katalog_zmiany, ale nie zmieniono_pliku.
Ken Kinder
@CadentOrange, jeśli nie podoba ci się zależnością pyqt, pakiet jest właściwą odpowiedziąwatchdog
Mike Pennington
dlaczego nie użyć PySidedo tego zamiast PyQtdo tak małego użytku.
Ciasto piekarz
29

Nie powinien on działać w systemie Windows (może z cygwinem?), Ale dla użytkownika unixa powinieneś użyć wywołania systemowego „fcntl”. Oto przykład w Pythonie. Jest to w większości ten sam kod, jeśli chcesz go napisać w C (te same nazwy funkcji)

import time
import fcntl
import os
import signal

FNAME = "/HOME/TOTO/FILETOWATCH"

def handler(signum, frame):
    print "File %s modified" % (FNAME,)

signal.signal(signal.SIGIO, handler)
fd = os.open(FNAME,  os.O_RDONLY)
fcntl.fcntl(fd, fcntl.F_SETSIG, 0)
fcntl.fcntl(fd, fcntl.F_NOTIFY,
            fcntl.DN_MODIFY | fcntl.DN_CREATE | fcntl.DN_MULTISHOT)

while True:
    time.sleep(10000)
Maxime
źródło
3
Działa jak urok z jądrem Linuksa 2.6.31 w systemie plików ext4 (na Ubuntu 10.04), choć tylko dla katalogów - podnosi IOError „nie katalog”, jeśli używam go z plikiem.
David Underhill
1
ŚWIETNY! To samo dla mnie, działa tylko dla katalogu i oglądaj pliki w tym katalogu. Ale nie będzie działać dla zmodyfikowanych plików w podkatalogach, więc wygląda na to, że musisz przejść przez podkatalogi i obejrzeć je wszystkie. (czy jest na to lepszy sposób?)
lfagundes,
20

Sprawdź pyinotify .

inotify zastępuje dnotify (z wcześniejszej odpowiedzi) w nowszych wersjach systemu Linux i umożliwia monitorowanie na poziomie pliku, a nie na poziomie katalogu.

Michael Palmer
źródło
5
Nie chcę tłumić tej odpowiedzi, ale po przeczytaniu tego artykułu powiedziałbym, że może nie być tak uroczym rozwiązaniem, jak myśl. serpentine.com/blog/2008/01/04/why-you-should-not-use-pyinotify
NuclearPeon
1
pyinotify ma wiele wad, od bardzo niepitonicznej bazy kodu po zużycie pamięci. Lepiej poszukać innych opcji.
Tyto
13

Po trochę włamania do skryptu Tima Goldena mam następujące, które wydają się działać całkiem dobrze:

import os

import win32file
import win32con

path_to_watch = "." # look at the current directory
file_to_watch = "test.txt" # look for changes to a file called test.txt

def ProcessNewData( newData ):
    print "Text added: %s"%newData

# Set up the bits we'll need for output
ACTIONS = {
  1 : "Created",
  2 : "Deleted",
  3 : "Updated",
  4 : "Renamed from something",
  5 : "Renamed to something"
}
FILE_LIST_DIRECTORY = 0x0001
hDir = win32file.CreateFile (
  path_to_watch,
  FILE_LIST_DIRECTORY,
  win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
  None,
  win32con.OPEN_EXISTING,
  win32con.FILE_FLAG_BACKUP_SEMANTICS,
  None
)

# Open the file we're interested in
a = open(file_to_watch, "r")

# Throw away any exising log data
a.read()

# Wait for new data and call ProcessNewData for each new chunk that's written
while 1:
  # Wait for a change to occur
  results = win32file.ReadDirectoryChangesW (
    hDir,
    1024,
    False,
    win32con.FILE_NOTIFY_CHANGE_LAST_WRITE,
    None,
    None
  )

  # For each change, check to see if it's updating the file we're interested in
  for action, file in results:
    full_filename = os.path.join (path_to_watch, file)
    #print file, ACTIONS.get (action, "Unknown")
    if file == file_to_watch:
        newText = a.read()
        if newText != "":
            ProcessNewData( newText )

Prawdopodobnie przydałoby się to przy sprawdzaniu większej ilości błędów, ale po prostu oglądając plik dziennika i wykonując na nim pewne przetwarzanie przed wyrzuceniem go na ekran, działa to dobrze.

Dziękujemy wszystkim za wkład - świetne rzeczy!

Jon Cage
źródło
10

Aby obejrzeć pojedynczy plik z odpytywaniem i minimalnymi zależnościami, oto w pełni rozwinięty przykład oparty na odpowiedzi Deestana (powyżej):

import os
import sys 
import time

class Watcher(object):
    running = True
    refresh_delay_secs = 1

    # Constructor
    def __init__(self, watch_file, call_func_on_change=None, *args, **kwargs):
        self._cached_stamp = 0
        self.filename = watch_file
        self.call_func_on_change = call_func_on_change
        self.args = args
        self.kwargs = kwargs

    # Look for changes
    def look(self):
        stamp = os.stat(self.filename).st_mtime
        if stamp != self._cached_stamp:
            self._cached_stamp = stamp
            # File has changed, so do something...
            print('File changed')
            if self.call_func_on_change is not None:
                self.call_func_on_change(*self.args, **self.kwargs)

    # Keep watching in a loop        
    def watch(self):
        while self.running: 
            try: 
                # Look for changes
                time.sleep(self.refresh_delay_secs) 
                self.look() 
            except KeyboardInterrupt: 
                print('\nDone') 
                break 
            except FileNotFoundError:
                # Action on file not found
                pass
            except: 
                print('Unhandled error: %s' % sys.exc_info()[0])

# Call this function each time a change happens
def custom_action(text):
    print(text)

watch_file = 'my_file.txt'

# watcher = Watcher(watch_file)  # simple
watcher = Watcher(watch_file, custom_action, text='yes, changed')  # also call custom action function
watcher.watch()  # start the watch going
4Oh4
źródło
2
Możesz tworzyć watch_filei tworzyć _cached_stamplisty oraz iterować je w pętli for. Nie bardzo dobrze skaluje się do dużej liczby plików
4Oh4
Czy to nie wyzwala akcji za każdym razem, gdy jest uruchamiana? _cached_stamp jest ustawiony na 0, a następnie porównywany z os.stat (self.filename) .st_mtime. _cached_stamp powinien być ustawiony w os.stat (self.filename) .st_mtime w konstruktorze, nie?
Seanonymous
1
call_func_on_change()zostanie uruchomiona przy pierwszym uruchomieniu look(), ale następnie _cached_stampzostanie zaktualizowana, więc nie zostanie uruchomiona ponownie, dopóki wartość os.stat(self.filename).st_mtime. _cached_stampzmian nie zostanie wprowadzona.
4O4
1
Możesz ustawić wartość _cached_stampw konstruktorze, jeśli nie chcesz call_func_on_change()być wywoływany przy pierwszym uruchomieniu
4Oh4,
Użyłem twojego skryptu, by wywołać jakąś funkcję przy zmianie pliku. Moja funkcja nie przyjmuje żadnych argumentów w przeciwieństwie do twojego. Pomyślałem, że żeby to zadziałało, muszę usunąć * args, ** kwargs. Wyglądało na to, że (wstawiam tylko linie ze zmianami): self.call_func_on_change(self) def custom_action(): watcher = Watcher(watch_file, custom_action())Ale to nie działało. Akcja została wywołana tylko podczas pierwszej iteracji: Plik zmieniony tak, zmieniony Plik zmieniony Plik zmieniony Plik zmieniony Zaczął działać, gdy zachowałem * argumenty i nazwał go: watcher = Watcher(watch_file, custom_action)Nie mogę się zastanawiać, dlaczego?
zwornik
7

Sprawdź moją odpowiedź na podobne pytanie . Możesz wypróbować tę samą pętlę w Pythonie. Ta strona sugeruje:

import time

while 1:
    where = file.tell()
    line = file.readline()
    if not line:
        time.sleep(1)
        file.seek(where)
    else:
        print line, # already has newline

Zobacz także pytanie tail () plik z Pythonem .

Bruno De Fraine
źródło
Możesz sys.stdout.write (linia). Twój kod nie działa, jeśli plik jest obcięty. Python ma wbudowaną funkcję file ().
jfs,
Opublikowałem zmodyfikowaną wersję twojego kodu. Możesz włączyć go do swojej odpowiedzi, jeśli to działa dla Ciebie.
jfs,
7

Najprostszym rozwiązaniem jest dla mnie narzędzie watchdog

Od https://pypi.python.org/pypi/watchdog Mam teraz proces, który wyszukuje pliki sql w katalogu i wykonuje je w razie potrzeby.

watchmedo shell-command \
--patterns="*.sql" \
--recursive \
--command='~/Desktop/load_files_into_mysql_database.sh' \
.
redestructa
źródło
6

Skoro używasz Pythona, możesz po prostu otworzyć plik i nadal czytać z niego wiersze.

f = open('file.log')

Jeśli odczytany wiersz nie jest pusty , przetwarzasz go.

line = f.readline()
if line:
    // Do what you want with the line

Być może brakuje Ci, że możesz nadal dzwonić readline do EOF. W takim przypadku po prostu zwróci pusty ciąg znaków. A kiedy coś zostanie dołączone do pliku dziennika, odczyt będzie kontynuowany od miejsca, w którym się zatrzymał, zgodnie z potrzebami.

Jeśli szukasz rozwiązania wykorzystującego zdarzenia lub określoną bibliotekę, określ to w swoim pytaniu. W przeciwnym razie myślę, że to rozwiązanie jest w porządku.

seuvitor
źródło
6

Oto uproszczona wersja kodu Kendera, która wydaje się robić tę samą sztuczkę i nie importuje całego pliku:

# Check file for new data.

import time

f = open(r'c:\temp\test.txt', 'r')

while True:

    line = f.readline()
    if not line:
        time.sleep(1)
        print 'Nothing New'
    else:
        print 'Call Function: ', line
AlaXul
źródło
6

Jest to kolejna modyfikacja skryptu Tima Goldana, która działa na typach unixowych i dodaje prostego obserwatora do modyfikacji plików za pomocą dict (file => time).

użycie: dowolnaNazwa.py ścieżka_do_katalogu_do_watch

#!/usr/bin/env python

import os, sys, time

def files_to_timestamp(path):
    files = [os.path.join(path, f) for f in os.listdir(path)]
    return dict ([(f, os.path.getmtime(f)) for f in files])

if __name__ == "__main__":

    path_to_watch = sys.argv[1]
    print('Watching {}..'.format(path_to_watch))

    before = files_to_timestamp(path_to_watch)

    while 1:
        time.sleep (2)
        after = files_to_timestamp(path_to_watch)

        added = [f for f in after.keys() if not f in before.keys()]
        removed = [f for f in before.keys() if not f in after.keys()]
        modified = []

        for f in before.keys():
            if not f in removed:
                if os.path.getmtime(f) != before.get(f):
                    modified.append(f)

        if added: print('Added: {}'.format(', '.join(added)))
        if removed: print('Removed: {}'.format(', '.join(removed)))
        if modified: print('Modified: {}'.format(', '.join(modified)))

        before = after
ronedg
źródło
Zaktualizowano do obsługi python3
ronedg
4

Jak widać w artykule Tima Golden'a , wskazanym przez Horsta Gutmanna , WIN32 jest stosunkowo złożony i obserwuje katalogi, a nie pojedynczy plik.

Chciałbym zasugerować zajrzenie do IronPython , który jest implementacją Pythona .NET . Dzięki IronPython możesz korzystać ze wszystkich funkcji .NET - w tym

System.IO.FileSystemWatcher

Który obsługuje pojedyncze pliki z prostym interfejsem Event .

gimel
źródło
@Ciasto, ponieważ wtedy musisz mieć dostęp do Iron Python zamiast podstawowej instalacji Pythona.
Jon Cage
1

To jest przykład sprawdzania pliku pod kątem zmian. Taki, który może nie jest najlepszym sposobem na zrobienie tego, ale z pewnością jest to krótka droga.

Przydatne narzędzie do ponownego uruchamiania aplikacji po wprowadzeniu zmian w źródle. Zrobiłem to podczas gry z pygame, dzięki czemu mogę zobaczyć efekty, które pojawiają się natychmiast po zapisaniu pliku.

Podczas używania w grze pygame upewnij się, że elementy w pętli „while” są umieszczone w pętli gry, czyli jako aktualizacja lub cokolwiek innego. W przeciwnym razie twoja aplikacja utknie w nieskończonej pętli i nie zobaczysz aktualizacji swojej gry.

file_size_stored = os.stat('neuron.py').st_size

  while True:
    try:
      file_size_current = os.stat('neuron.py').st_size
      if file_size_stored != file_size_current:
        restart_program()
    except: 
      pass

Na wypadek, gdybyś chciał ponownie uruchomić kod, który znalazłem w sieci. Oto jest. (Nie dotyczy pytania, choć może się przydać)

def restart_program(): #restart application
    python = sys.executable
    os.execl(python, python, * sys.argv)

Baw się dobrze, gdy elektrony robią to, co chcesz.

Bassim Huis
źródło
Wydaje się, że użycie .st_mtimezamiast .st_sizebyłoby bardziej niezawodnym i równie krótkim sposobem na zrobienie tego, chociaż PO wskazał, że nie chce tego robić przez ankietę.
martineau
1
ACTIONS = {
  1 : "Created",
  2 : "Deleted",
  3 : "Updated",
  4 : "Renamed from something",
  5 : "Renamed to something"
}
FILE_LIST_DIRECTORY = 0x0001

class myThread (threading.Thread):
    def __init__(self, threadID, fileName, directory, origin):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.fileName = fileName
        self.daemon = True
        self.dir = directory
        self.originalFile = origin
    def run(self):
        startMonitor(self.fileName, self.dir, self.originalFile)

def startMonitor(fileMonitoring,dirPath,originalFile):
    hDir = win32file.CreateFile (
        dirPath,
        FILE_LIST_DIRECTORY,
        win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
        None,
        win32con.OPEN_EXISTING,
        win32con.FILE_FLAG_BACKUP_SEMANTICS,
        None
    )
    # Wait for new data and call ProcessNewData for each new chunk that's
    # written
    while 1:
        # Wait for a change to occur
        results = win32file.ReadDirectoryChangesW (
            hDir,
            1024,
            False,
            win32con.FILE_NOTIFY_CHANGE_LAST_WRITE,
            None,
            None
        )
        # For each change, check to see if it's updating the file we're
        # interested in
        for action, file_M in results:
            full_filename = os.path.join (dirPath, file_M)
            #print file, ACTIONS.get (action, "Unknown")
            if len(full_filename) == len(fileMonitoring) and action == 3:
                #copy to main file
                ...
chochlik
źródło
1

Oto przykład ukierunkowany na oglądanie plików wejściowych, które zapisują nie więcej niż jedną linię na sekundę, ale zwykle o wiele mniej. Celem jest dołączenie ostatniego wiersza (najnowszego zapisu) do określonego pliku wyjściowego. Skopiowałem to z jednego z moich projektów i właśnie usunąłem wszystkie niepotrzebne wiersze. Musisz wypełnić lub zmienić brakujące symbole.

from PyQt5.QtCore import QFileSystemWatcher, QSettings, QThread
from ui_main_window import Ui_MainWindow   # Qt Creator gen'd 

class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        Ui_MainWindow.__init__(self)
        self._fileWatcher = QFileSystemWatcher()
        self._fileWatcher.fileChanged.connect(self.fileChanged)

    def fileChanged(self, filepath):
        QThread.msleep(300)    # Reqd on some machines, give chance for write to complete
        # ^^ About to test this, may need more sophisticated solution
        with open(filepath) as file:
            lastLine = list(file)[-1]
        destPath = self._filemap[filepath]['dest file']
        with open(destPath, 'a') as out_file:               # a= append
            out_file.writelines([lastLine])

Oczywiście obejmująca klasa QMainWindow nie jest ściśle wymagana, tj. możesz użyć samego QFileSystemWatcher.


źródło
0

Możesz także użyć prostej biblioteki o nazwie repyt , oto przykład:

repyt ./app.py
Rafał Enden
źródło
0

Wygląda na to, że nikt nie opublikował fswatch . Jest wieloplatformowym obserwatorem systemu plików. Po prostu zainstaluj, uruchom i postępuj zgodnie z instrukcjami.

Użyłem go z programami python i golang i to po prostu działa.

Gus
źródło
0

powiązane rozwiązanie @ 4Oh4 płynna zmiana listy plików do obejrzenia;

import os
import sys
import time

class Watcher(object):
    running = True
    refresh_delay_secs = 1

    # Constructor
    def __init__(self, watch_files, call_func_on_change=None, *args, **kwargs):
        self._cached_stamp = 0
        self._cached_stamp_files = {}
        self.filenames = watch_files
        self.call_func_on_change = call_func_on_change
        self.args = args
        self.kwargs = kwargs

    # Look for changes
    def look(self):
        for file in self.filenames:
            stamp = os.stat(file).st_mtime
            if not file in self._cached_stamp_files:
                self._cached_stamp_files[file] = 0
            if stamp != self._cached_stamp_files[file]:
                self._cached_stamp_files[file] = stamp
                # File has changed, so do something...
                file_to_read = open(file, 'r')
                value = file_to_read.read()
                print("value from file", value)
                file_to_read.seek(0)
                if self.call_func_on_change is not None:
                    self.call_func_on_change(*self.args, **self.kwargs)

    # Keep watching in a loop
    def watch(self):
        while self.running:
            try:
                # Look for changes
                time.sleep(self.refresh_delay_secs)
                self.look()
            except KeyboardInterrupt:
                print('\nDone')
                break
            except FileNotFoundError:
                # Action on file not found
                pass
            except Exception as e:
                print(e)
                print('Unhandled error: %s' % sys.exc_info()[0])

# Call this function each time a change happens
def custom_action(text):
    print(text)
    # pass

watch_files = ['/Users/mexekanez/my_file.txt', '/Users/mexekanez/my_file1.txt']

# watcher = Watcher(watch_file)  # simple



if __name__ == "__main__":
    watcher = Watcher(watch_files, custom_action, text='yes, changed')  # also call custom action function
    watcher.watch()  # start the watch going
mexekanez
źródło
0

Najlepszym i najprostszym rozwiązaniem jest użycie pygtail: https://pypi.python.org/pypi/pygtail

from pygtail import Pygtail
import sys

while True:
    for line in Pygtail("some.log"):
        sys.stdout.write(line)
Jerzy
źródło
-2

Nie znam żadnej funkcji specyficznej dla systemu Windows. Możesz spróbować uzyskać skrót MD5 pliku co sekundę / minutę / godzinę (w zależności od tego, jak szybko go potrzebujesz) i porównać go z ostatnim skrótem. Kiedy różni się, wiesz, że plik został zmieniony i czytasz najnowsze wiersze.

skalowalny
źródło
-6

Spróbowałbym czegoś takiego.

    try:
            f = open(filePath)
    except IOError:
            print "No such file: %s" % filePath
            raw_input("Press Enter to close window")
    try:
            lines = f.readlines()
            while True:
                    line = f.readline()
                    try:
                            if not line:
                                    time.sleep(1)
                            else:
                                    functionThatAnalisesTheLine(line)
                    except Exception, e:
                            # handle the exception somehow (for example, log the trace) and raise the same exception again
                            raw_input("Press Enter to close window")
                            raise e
    finally:
            f.close()

Pętla sprawdza, czy są nowe linie od ostatniego odczytu pliku - jeśli istnieje, jest on odczytywany i przekazywany do functionThatAnalisesTheLinefunkcji. Jeśli nie, skrypt czeka 1 sekundę i ponawia proces.

kender
źródło
4
-1: Otwieranie pliku i czytanie linii nie jest świetnym pomysłem, gdy pliki mogą mieć wielkość 100 MB. Będziesz musiał uruchomić go dla każdego pliku, co byłoby złe, gdy chcesz obejrzeć 1000 plików.
Jon Cage
1
Naprawdę? Otwieranie pliku zmian?
Farsheed,