It is currently March 29th, 2023, 10:44 am

TailFile

Skins that control functions in Windows or Rainmeter
User avatar
jsmorley
Developer
Posts: 22564
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

TailFile

Post by jsmorley »

This skin demonstrates how you can "tail" some number of lines from a text file, like a log file or to-do list or any other file you would like to see some number of the "most recent" items added to the bottom of the file, in reverse order.

This uses a very simple Lua script to read the file. The Lua then sets some number of String measures in the skin that are in a sense acting as "child" measures to the Lua Script measure. The meters then use the values of those String measures to display the lines.

You can simply change the file you want to read in [Variables] and use this as is, or feel free to tear it apart to use the underlying functionality in your skin.

I think it is pretty easy to follow what it is doing, but feel free to ask any questions.
TailFile_2.0.rmskin
1.jpg
Note that you easily could add IfMatch conditions to the String measures, which would for instance allow you to monitor for "error" in a log file, and set a font color different or any other action you like. You could also use RegExpSubstitute to replace text in the line, so for instance you could remove unwanted tabs or spaces, remove the first "xx" characters of the line, or any other change you might want to make with a regular expression search-and-replace.


Important: If you are going to have Unicode characters in the text file you are reading, that file MUST be encoded in UTF-8 with or without BOM. That is the only encoding supported by Lua when reading external Unicode files.
You do not have the required permissions to view the files attached to this post.
User avatar
jsmorley
Developer
Posts: 22564
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: TailFile

Post by jsmorley »

Updated the Lua code to use a vastly more efficient method for reading the last N lines of a large file. The Lua code is based on work by Brian in This post.
PickNick
Posts: 1
Joined: February 23rd, 2022, 9:56 am

Re: TailFile

Post by PickNick »

Hi jsmorley,

thanks for the great skin!
It looks very promising. I was trying to tweak it to my purpose and I noticed during testing that it crashes the whole RainMeter when the source file has no data. Any idea how to prevent this?

Regards,
PickNick
User avatar
ikarus1969
Posts: 552
Joined: February 28th, 2011, 3:20 pm
Location: Vienna, Austria

Re: TailFile

Post by ikarus1969 »

As it is about 2 months since you asked for preventing rainmeter crashing TailFile i had a look at it and made a change to TailFile.lua which just do nothing with the skin but returning -1 if it is a file with 0 bytes.

Technically the seek-function returns nil which i query. As the forum prevents lua-files to be attached to posts you have to copy/paste the content by yourself. Maybe there are some more clever lua-solutions to this but it is just straight forward.

Code: Select all

function Initialize()
      
  fileToRead = SKIN:GetVariable('FileToRead')
  linesToTail = tonumber(SKIN:GetVariable('LinesToTail'))
  childPrefix = SKIN:GetVariable('ChildPrefix')

end

function Update()
   
  local inputFile = assert(io.open(fileToRead, 'r'))
  local singleChar, lineText
  local bytePosition = -1
  local i = 1
  local tooFewLines = false

  repeat
    if inputFile:seek('end', bytePosition - 1) == nil then
      tooFewLines = true;
    else
      singleChar = inputFile:read(1)
      if singleChar == '\n' then
        lineText = inputFile:read(-bytePosition)
        if lineText then
          lineText = string.gsub(lineText, '\r', '')
          SKIN:Bang('!SetOption', childPrefix..i, 'String', string.match(lineText, '^(.-)\n'))
          SKIN:Bang('!UpdateMeasure', childPrefix..i)
          i = i + 1
          bytePosition = bytePosition - 1
        end
      end
      bytePosition = bytePosition - 1
    end
  until (tooFewLines or i > linesToTail)

  io.close(inputFile)

  SKIN:Bang('!UpdateMeter', '*')
  SKIN:Bang('!Redraw')

  return 1
  
end