Content-based file search with Powershell and FileLocator

I love Powershell. Unfortunately, as soon as we cross into the realm of trying to grep for a specific string in gigabytes worth of large files, Powershell becomes a bit of a slowpoke.

Thankfully I also use the incredible FileLocator Pro, a highly optimized tool for searching file contents – no matter the size. The search is blazingly fast – and you can easily utilize FileLocator’s magic within Powershell!

For the sake of clarity: I will be using Powershell 7.1.3 for the following example.

# Add the required assembly
Add-Type -Path "C:\Program Files\Mythicsoft\FileLocator Pro\Mythicsoft.Search.Core.dll"

# Prepare the base search engine and criteria
$searchEngine                      = New-Object Mythicsoft.Search.Core.SearchEngine
$searchCriteria                    = New-Object Mythicsoft.Search.Core.SearchFileSystemCriteria

$searchCriteria.FileName           = "*.log"
$searchCriteria.FileNameExprType   = [Mythicsoft.Search.Core.ExpressionType]::Boolean

$searchCriteria.LookIn             = "C:\Temp\LogData"
$searchCriteria.LookInExprType     = [Mythicsoft.Search.Core.ExpressionType]::Boolean

$searchCriteria.SearchSubDirectory = $true

$searchCriteria.ContainingText     = ".*The device cannot perform the requested procedure.*"
$searchCriteria.ContentsExprType   = [Mythicsoft.Search.Core.ExpressionType]::RegExp

# Actually perform the search, $false executes it on the same thread as the Powershell session (as in: it's blocking)
$searchEngine.Start($searchCriteria, $false)

foreach($result in $searchEngine.SearchResultItems)
{
   # SeachResultItems are on a per-file basis.
   foreach($line in $result.FoundLines)
   {
      "Match in $($result.FileName) on line $($line.LineNumber): $($line.Value)"
   }
}

Wowzers, that’s pretty easy! In fact, a lot easier (and quicker, to boot!) than playing around with Get-Contents, StreamReaders and the like.

One thing of note here: Between running this on a loop for every file in a directory, it is actually quicker to process an entire tree of folders/files. The larger the dataset, the larger the gains through invoking FileLocator.

And yeah, you can use FileLocator on the command line through flpsearch.exe – however the results are not as easily digestable as the IEnumerables you get through the assembly.

ZNC Playback script for mIRC/AdiIRC

The main reason I use an IRC bouncer is so I can detach from the bouncer and get the messages I missed the next time I attach to it again. ZNC provides support for this feature by default, however, there is a third-party module called Playback that has some additional bells and whistles.

To properly utilize the Playback module, you need to adjust two settings on your bouncer and your IRC client needs to do some minor lifting. After searching the internet far and wide, I have not come across a premade AdiIRC script that worked the way I wanted it to, so I figured it was high time to improve the situation.

So what do we actually need to teach our IRC client? Essentially, the client needs to keep track of when it received the network’s last message, so it can request all newer messages that are newer than this timestamp from the bouncer upon reconnect. Sounds easy enough, especially since there were some example scripts for other clients linked on the wiki page for Playback.

I wired up a basic mIRC/AdiIRC script that will retain timestamps of ZNC connections on a per-network basis. Instead of merely updating the timestamp when a PRIVMSG comes in, the script also updates the timestamp on JOIN/PART events to cover “quiet” channels/networks.

To avoid the odd timezone problems, the script will read the timestamp from IRCv3 enabled timestamp parts within events/messages. I still have some odd timezone issues between my own IRCd, bouncer and client, but this is likely due to a configuration problem on my end. On the major networks, the script operates as intended. The data is held in a small hashtable that gets serialized/deserialized to an INI file on exit/startup.