Мониторинг файлов PowerShell и преобразование текста в речь - PullRequest
0 голосов
/ 10 декабря 2018

Я новичок в PowerShell и пытаюсь помочь другу написать сценарий, который будет постоянно следить за файлом, и всякий раз, когда файл изменяется, новый текст в файле читается вслух (текстовый файл постоянно редактируется и всестарого контента внутри него заменяется новым контентом, который берется из писем по мере их поступления).

Сценарий отлично работает, когда извлекает содержимое из файла и читает его вслух, но у меня есть одна небольшая проблема: он читает содержимое два или четыре раза, в то время как он мне нужен только дляпрочитайте это однажды.

Кроме того, в то время как PowerShell говорит о содержимом, он не обновляет / не ставит в очередь изменения в файле, поэтому, если два изменения были сделаны, в то время как PowerShell говорит о предыдущем изменении, первое изменение пропускается и толькопоследнее изменение читается вслух.Есть ли способ заставить его ставить в очередь все изменения файла и читать их последовательно?

В настоящее время у меня есть

Add-Type -AssemblyName System.speech
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer
$speak.Rate = 0 # -10 is slowest, 10 is fastest

$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "C:\Users\Dylan\Desktop\"
$watcher.Filter = "alarm.txt"
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $true

$AlarmLocation = "C:\Users\Dylan\Desktop\alarm.txt"

$changeAction = {
    $Alarm = (Get-Content $AlarmLocation)
    $speak.Speak($Alarm)
}

Register-ObjectEvent $watcher "Changed" -Action $changeAction
while ($true) {sleep 5}

Я что-то упускаю здесь очевидное или есть другая функцияЯ должен включить?

Спасибо

1 Ответ

0 голосов
/ 10 декабря 2018

Проблема с несколькими событиями объясняется здесь: https://blogs.msdn.microsoft.com/oldnewthing/20140507-00/?p=1053/

Один из способов справиться с этим - отслеживать LastWriteTime.

Мы можем запустить динамик в другом потоке,так что это не блокирует наблюдателя.Таким образом, мы обнаружим, что файл изменяется во время разговора.

Как-то так ...

# When the file is changed,
# the content is stored in the queue,
# and the speaker is signaled.

# it breaks, if the file changes very rapidly.

# use a hashtable for all the vars
# for easier transport across scopes
$vars = [hashtable]::Synchronized(@{})
$vars.speakQueue = New-Object System.Collections.Queue
$vars.speakEvent = New-Object System.Threading.AutoResetEvent $false
$vars.speakLastWriteTime = [DateTime]::MinValue
$vars.speakRunning = $true

$vars.speakPS = [System.Management.Automation.PowerShell]::Create().AddScript({
    # this is the speaker thread
    Param (
        $vars
    )

    Add-Type -AssemblyName System.speech
    $speak = New-Object System.Speech.Synthesis.SpeechSynthesizer
    $speak.Rate = 0 # -10 is slowest, 10 is fastest

    # run until other thread sets running=false
    while($vars.speakRunning) {
        # other thread sets the event when content is available
        if($vars.speakEvent.WaitOne(100)) {
            # use System.Threading.Monitor to make queue thread safe
            [System.Threading.Monitor]::Enter($vars.SyncRoot)
            try {
                # get all alarms
                $alarm = while($vars.speakQueue.Count){ $vars.speakQueue.Dequeue() }
            }
            catch {
            }
            [System.Threading.Monitor]::Exit($vars.SyncRoot)

            # speak now
            $alarm | ForEach-Object { $speak.Speak($_) }
        }
    }
}).AddArgument($vars)

# start new thread
$vars.speakPSHandle = $vars.speakPS.BeginInvoke()

$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "C:\Users\Dylan\Desktop\"
$watcher.Filter = "alarm.txt"
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $true
$watcher.NotifyFilter = [System.IO.NotifyFilters]::LastWrite

$changeAction = {
    $vars = $event.MessageData

    # FullPath is the path of the changed file
    $item = Get-Item $event.SourceEventArgs.FullPath

    # only proceed, if LastWriteTime has changed
    if($item.LastWriteTime -ne $vars.speakLastWriteTime) {
        $vars.speakLastWriteTime = $item.LastWriteTime

        $alarm = Get-Content $event.SourceEventArgs.FullPath -Raw

        [System.Threading.Monitor]::Enter($vars.SyncRoot)
        try {
            # put content in queue
            $vars.speakQueue.Enqueue($alarm)
        }
        catch {
        }
        [System.Threading.Monitor]::Exit($vars.SyncRoot)

        # signal speaker in other thread
        $vars.speakEvent.Set()
    }
}

$job = Register-ObjectEvent $watcher "changed" -Action $changeAction -SourceIdentifier "FileChanged" -MessageData $vars

while($true) {
    Start-Sleep -Milliseconds 25
}

# clean-up, if ever needed...
Unregister-Event "FileChanged"
$vars.speakRunning = $false # leaves while-loop in thread
$vars.speakPS.EndInvoke($vars.speakPSHandle) # waits for thread to end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...