C как использовать модуль power shell file system watcher

You can use a FileSystemWatcher for just this task, it is what it is built for.

An a full sample implementation of the watcher.
https://powershell.one/tricks/filesystem/filesystemwatcher

Above is a powershell implementation that will raise an event to let you know if a file was added, deleted, modified, etc. Form there you can use whatever messaging method you like when an event is detected, you can use Send-MailMessage, it still works but it slated for obsolescence, so no idea how long it will stick around.

So you could make the break and go straight to System.Net.Mail as well which should be long term supportable.

Function Send-Email { [CmdletBinding()] param ( [String]$From, [String[]]$To, [String]$Subject, [String]$SMTPServer, [String]$Body, [String[]]$Attachments ) $email = New-Object System.Net.Mail.MailMessage $From, $To, $Subject, $Body $email.To.Add($To) $email.isBodyhtml = $true $smtp = new-object Net.Mail.SmtpClient($SMTPServer) $smtp.Port = 25 $emailAttachment = new-object Net.Mail.Attachment($Attachments) $email.Attachments.Add($emailAttachment) $smtp.Send($email)
}

Processing log lines

OK, good enough. But now we’re here, we get more ambitious. I want to display the name of the log file in a different color and in general, add some processing to the output, so we go all in:

[Cmdletbinding()]
Param(
[Parameter(Mandatory)]
[PSObject[]]$Path,
[int]$Tail
)

So now we can do other things: color the output of the log files for instance, depending on the contents of the line or even the name of the log file. I’ll leave that to the imagination of the reader. Just one tip: make sure the output of is written in one statement. If you split it up into multiple Write-Host -Foreground $color calls, for instance, each with a different color, you’ll end up with partial lines because another log file’s log line will be written to the output at the same time. Using ANSI escape sequences (in a single line!) prevents this:

function ProcessLogLine {
Param($logfile, $line)
"$($PSStyle.Foreground.Yellow)$($logfile.Name)$($PsStyle.Reset): $line"
}

So, knock yourself out experimenting with nice color schemes – I know I did 😉

:/>  Cmd файлов. Работа со строками

Using the pipeline

Step 1 in updating the script to obtain the log files from the pipeline is the obvious one: add to the parameter.

[Cmdletbinding()]
Param(
[Parameter(Mandatory, ValueFromPipeline)]
[PSObject[]]$Path,
[int]$Tail
)
...

As usual, it’s not that simple. When we pass a log file to the script this way, it works, but as soon as we add more, it falls apart. Upon closer inspection it’s obvious why. When the first log file enters the script through the pipeline, we start monitoring it until the script is terminated. This does not leave room for the second log file, or the next.

So we have to resort to good old . The plan is simple: we collect each item from the pipeline with and , then start monitoring all of them at . Not very intuitive (the part actually does the processing), but once again, it works:

[Cmdletbinding()]
Param(
[Parameter(Mandatory, ValueFromPipeline)]
[PSObject[]]$Path,
[int]$Tail
)

so we can finally do

(Get-ChildItem *.log) | .\Get-LogTail.ps1 -Tail 5

OK, back to work.

Blog of Tools

Saturday, August 5, 2023

The case

Traditionally (i.e. in Linux) the tail command is used to monitor a log file. (Or any text file for that matter). At heart, it shows the “log tail”, i.e. the last n lines of the file. But it can also periodically check for added lines and display them, too. PowerShell has this ability as well, using the cmdlet with the switch.

To monitor our four log files, we can start four PowerShell prompts and let them run on a particular log file each. That works, but it’s clumsy: you have to arrange the individual windows so they’re all visible, and you still need to scan four windows for the request you’re tracking.

:/>  Как увидеть все файлы на флешке

I searched for “viewing multiple log files” and there are some solutions but they’re mostly for Linux (I need Windows) and none of them seems to quite do what I want. Either that, or they’re much too powerful and require a subscription :-/

The solution

So here we go:

Get-ChildItem *.log |
ForEach-Object -Parallel {
Get-Content $_ -Wait -Tail 5
}

Works exactly as expected. It displays the last five lines of each log file, and then each line in each log file that’s added later, until you stop it with .

But wait a minute – I can’t see which log file each line belongs to! Sure, I can track my request now – the relevant log lines are shown, regardless of which server handled it, but when I spot the problem I have to figure out which log file the line belongs to.

Fortunately, this is easily remedied: we just send the output of to , prefixed with the file name:

Get-ChildItem *.log |
ForEach-Object -Parallel {
$log = $_
Get-Content $log -Wait -Tail 2 |
ForEach-Object {
Write-Output "$($log.Name): $_"
}
}

To make everything a little more configurable, we make the log files themselves an input to our cmdlet and we make the value a parameter. And while we’re at it, we’ll call the whole contraption :

[Cmdletbinding()]
Param(
[Parameter(Mandatory)]
[string[]]$Path,
[int]$Tail
)
[Cmdletbinding()]
Param(
  [Parameter(Mandatory)]
  [string[]]$Path,
  [int]$Tail
)

(Aside: This works just fine but we have to specify as an argument, as in

.\Get-LogTail.ps1 -Path (Get-ChildItem *.log)

when it’s much more natural to pipe the input in, like:

(Get-ChildItem *.log) | .\Get-LogTail.ps1

But this requires the  parameter to come from the pipeline and that sort of screws up the whole thing. There’s a solution at the end of this page, but we’ll keep it simple for now.)

[Cmdletbinding()]
Param(
  [Parameter(Mandatory)]
  [PSObject[]]$Path,
  [int]$Tail
)

The hunch

Let’s start with :

Get-Content [-Path] <string[]> [-ReadCount <long>] [-TotalCount <long>]
[-Tail <int>] [-Filter <string>] [-Include <string[]>] [-Exclude <string[]>]
[-Force] [-Credential <pscredential>] [-Delimiter <string>] [-Wait] [-Raw]
[-Encoding <Encoding>] [-AsByteStream] [-Stream <string>] [<CommonParameters>]

Mimicking Linux’ , you use it like:

Get-Content some.log -Tail 20 -Wait

This will display the last 20 lines of the file and then check every second it more lines were added and show them, too. This only ends when you press .

:/>  Скачать iso образ Windows 10 x64 - 32 bit 1909 Офис 2019 активированная

But wait a minute: the argument handles an array of strings! How does that work? Well, not like we want. Without the switch: fine. It displays the tails of the specified log files and exits. With ? Not so much – it just ignores all but the first log file.

ForEach-Object -Parallel { <script> }

which is, of course, totally brilliant for this problem.