How do I watch a file for changes?

前端 未结 25 2759
孤街浪徒
孤街浪徒 2020-11-21 07:13

I have a log file being written by another process which I want to watch for changes. Each time a change occurs I\'d like to read the new data in to do some processing on it

相关标签:
25条回答
  • 2020-11-21 07:48

    Well after a bit of hacking of Tim Golden's script, I have the following which seems to work quite well:

    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 )
    

    It could probably do with a load more error checking, but for simply watching a log file and doing some processing on it before spitting it out to the screen, this works well.

    Thanks everyone for your input - great stuff!

    0 讨论(0)
  • 2020-11-21 07:48

    Check my answer to a similar question. You could try the same loop in Python. This page suggests:

    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
    

    Also see the question tail() a file with Python.

    0 讨论(0)
  • 2020-11-21 07:48

    I don't know any Windows specific function. You could try getting the MD5 hash of the file every second/minute/hour (depends on how fast you need it) and compare it to the last hash. When it differs you know the file has been changed and you read out the newest lines.

    0 讨论(0)
  • 2020-11-21 07:49

    As you can see in Tim Golden's article, pointed by Horst Gutmann, WIN32 is relatively complex and watches directories, not a single file.

    I'd like to suggest you look into IronPython, which is a .NET python implementation. With IronPython you can use all the .NET functionality - including

    System.IO.FileSystemWatcher
    

    Which handles single files with a simple Event interface.

    0 讨论(0)
  • 2020-11-21 07:50

    Well, since you are using Python, you can just open a file and keep reading lines from it.

    f = open('file.log')
    

    If the line read is not empty, you process it.

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

    You may be missing that it is ok to keep calling readline at the EOF. It will just keep returning an empty string in this case. And when something is appended to the log file, the reading will continue from where it stopped, as you need.

    If you are looking for a solution that uses events, or a particular library, please specify this in your question. Otherwise, I think this solution is just fine.

    0 讨论(0)
  • 2020-11-21 07:51

    related @4Oh4 solution a smooth change for a list of files to watch;

    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
    
    0 讨论(0)
提交回复
热议问题