Главная > Powershell > Подписка на события ОС в Powershell

Подписка на события ОС в Powershell

Powershell умеет подписываться на события, происходящие в операционной системе и реагировать на них выполнением определённого кода. Что позволяет запускать скрипты не только вручную, или по расписанию, но и по факту возникновения какого-либо события. Событием может быть, например, возникновение процесса, изменение/удаление файла и т. д. За каждую область событий отвечает свой провайдер. Так, за появлением процессов следит ManagementEventWatcher, а файловую систему мониторит FileSystemWatcher.

Ниже пойдёт речь о мониторинге каталога на предмет создания в нём файлов определённого типа.

Это лишь один из способов следить за файловой системой, который я увидел на одном забугорном сайте, и решил им поделиться.

Для начала определим каталог, за которым будем следить, и таймаут ожидания события:

# Каталог, в котором будем следить за файлами
$Path = "D:\Doc"

# Таймаут (в мс.)
$Timeout = 1000

 Далее создаём объект и задаём нужные нам свойства: 

# Создаём объект, который будет следить за возникновением события
$Watcher = New-Object System.IO.FileSystemWatcher

# Задаём путь к каталогу, за которым нужно следить
$Watcher.Path = $Path

# При необходимости задаём фильтр
$Watcher.Filter = "*.mp3"

Вызываем метод WaitForChanged, в котором указываем событие, которое мы ждём, и таймаут до завершения ожидания:

$Result = $Watcher.WaitForChanged("created", $Timeout)

Этот метод ожидает до тех пор, пока не произойдёт событие, или же не истечёт время ожидания (если задать $Timeout = –1, то ожидание будет длиться бесконечно).

Наиболее распространённые события:

  • Changed — Изменение файла или каталога.
  • Открытое событиеCreated — Создание файла или каталога.
  • Открытое событиеDeleted — Удаление файла или каталога.
  • Renamed — Переименование файла или каталога.

В нашём случае метод будет ждать либо появления в указанной папке .mp3 файла, либо истечение таймаута в 1 сек.

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

do
{
    $Result = $Watcher.WaitForChanged("created", $Timeout)

    # Значение true, если время ожидания метода WaitForChanged истекло;
    # в противном случае — значение false.
    if ($Result.TimedOut -eq $false)
    {
        [System.Windows.Forms.MessageBox]::Show($Result.Name,
        "Появился новый файл", [System.Windows.Forms.MessageBoxButtons]::OK)
    }
}
until ([System.Console]::KeyAvailable)

В результате работы скрипта при появлении файлов mp3 в каталоге D:\Doc на экране будет появляться сообщение, информирующее о появлении нового файла:

test.mp3

Рубрики:Powershell Метки: ,
  1. 007
    15/01/2018 в 22:58

    Я не програмист
    Дайте пожалуста целий скрипт.
    Или если всьо слепить будет готовий скрипт?
    Ответь пожалуста.

    • 16/01/2018 в 09:51

      Если собрать всё вместе получится скрипт. Только нужно учитывать, что скрипт из статьи будет мониторить появление только mp3-файлов, в каталоге, указанном в переменной $Path. Измените условия под себя.

  2. Виктория
    30/10/2018 в 12:11

    Добрый день! Спасибо за скрипт. Он отлично работает.
    Моя задача следующая:
    Появляется новый файл. Закупается сторонний процесс и переносит мой новый файл в подкаталог. Затем снова ждет новый файлик, чтобы повторить процедуру.

    Ваш скрипт запоминает документы в папке. срабатывает при появлении нового документа. Но после переноса в подкаталог он прекращает свою работу и больше не ждет ничего нового.

    Как мне быть?

    • 31/10/2018 в 10:06

      Добрый день! Как вариант, Вам нужно зациклить этот код. Думаю, что проще будет оформить его в функцию, и зациклить вызов функции

  3. alex
    22/04/2019 в 16:40

    добрый день, скопировал строки в Мой_файл.ps1, но он не запускается. Куча ошибок- с чем это может быть связано? Ошибки типа- отсутствует скобка, где-то- запятая. почему?

    • 23/04/2019 в 13:03

      Внимательно посмотрел пример из статьи. Скопировал в редактор — всё ок.
      Либо Вы не всё скопировали, либо как вариант у вас такой же интересный глюк, как у меня после последнего обновления системы (уже несколько дней наблюдаю): при вставке текста из буфера в консоль через щелчок правой кнопкой теряются заглавные символы. Т.е., например, вместо строки

      $Watcher = New-Object System.IO.FileSystemWatcher

      получается

      $atcher = ew-bject ystem..ileystematcher

      Но при этом вставка через Ctrl+V нормально работает.
      Или у Вас какой-то совсем уникальный случай. Если что скиньте саму ошибку, попробуем разобраться вместе.

      • alex
        23/04/2019 в 13:37

        Спасибо за ответ! я записал в се в PSIce, убрал пробелы из строки 15 и она заработала (отдельная строка). Но теперь ошибка в 19-й строке.

        PS C:\Windows\system32> until ([System.Console.In.Peek]::KeyAvailable)
        Unable to find type [System.Console.In.Peek].
        At line:1 char:13
        + until ([System.Console.In.Peek]::KeyAvailable)
        + ~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : InvalidOperation: (System.Console.In.Peek:TypeName) [], RuntimeException
        + FullyQualifiedErrorId : TypeNotFound

        Вобщем по-прежнему ничего не работает(

      • alex
        23/04/2019 в 13:42

        Извините, вот правильная ссылка на ошибку. в предыдущем случае я попытался изменить команду на Console.In.Peek)

        PS C:\Windows\system32> until ([System.Console]::KeyAvailable)
        Невозможно отследить нажатие клавиши, когда любое приложение не имеет консоли либо ввод данных на консол
        и был переадресован из файла. Попробуйте Console.In.Peek.
        At line:1 char:6
        + until ([System.Console]::KeyAvailable)
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : OperationStopped: (:) [], InvalidOperationException
        + FullyQualifiedErrorId : System.InvalidOperationException

      • alex
        23/04/2019 в 13:52

        Подумал, что вам будет удобнее, если я пришлю ссылку

        https://drive.google.com/open?id=1ZLJ-htmf_GkurLTy3db6xCKeIa27kxaY

  4. alex
    23/04/2019 в 13:40

    Извините, вот правильная ссылка на ошибку. В предыдущем случае я пробовал изменить команду на Console.In.Peek

    PS C:\Windows\system32> until ([System.Console]::KeyAvailable)
    Невозможно отследить нажатие клавиши, когда любое приложение не имеет консоли либо ввод данных на консол
    и был переадресован из файла. Попробуйте Console.In.Peek.
    At line:1 char:6
    + until ([System.Console]::KeyAvailable)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OperationStopped: (:) [], InvalidOperationException
    + FullyQualifiedErrorId : System.InvalidOperationException

    • 23/04/2019 в 14:19

      Вы запускаете скрипт из оболочки (PS ISE). Сохраните скрипт в файл и запустите из консоли.

      Только тогда скорее всего скрипт ругнётся на то, что ничего не знает о формах (System.Windows.Forms), но при этом будет работать. Если Вы не убрали вывод сообщения, нужно подгрузить сборку для форм.
      Где-нибудь до вызова формы (и не в цикле) добавьте вот такую строку:

      [void][system.reflection.assembly]::LoadWithPartialName(«system.windows.Forms»)

  5. alex
    23/04/2019 в 14:37

    не работает. вы уж меня простите за назойливость
    https://drive.google.com/open?id=1l0xexTBS3HaaEwTVN93Tu0VP3GFneYiM

    • alex
      23/04/2019 в 15:00

      кавычки я поменял, этого текста ошибки больше нет, но.. и программа не работает

      • 23/04/2019 в 16:13

        Подозрительные у Вас кавычки в 8-й строке, ну ок , а что не работает? На что ругается?

  6. Рома Карпов
    19/08/2020 в 22:11

    Здравствуйте, что то не получается у меня этот скрипт завести, ругается на эту строчку когда в папке добавляю файл с расширением который я указал «Не удалось найти тип [System.Windows.Forms.MessageBox].
    C:\Users\ROMAN\Desktop\bat1\delliteold\Безымянный1.ps1:13 знак:9
    + [System.Windows.Forms.MessageBox]::Show($Result.Name,
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (System.Windows.Forms.MessageBox:TypeName) [], RuntimeException
    + FullyQualifiedErrorId : TypeNotFound», Вам не трудно помочь?
    Сам скрипт вот в таком виду;
    $Path = «C:\Users\ROMAN\Desktop\1»
    $Timeout = 1000
    $Watcher = New-Object System.IO.FileSystemWatcher
    $Watcher.Path = $Path
    $Watcher.Filter = «*.dcm»
    $Result = $Watcher.WaitForChanged(«created», $Timeout)
    do
    {
    $Result = $Watcher.WaitForChanged(«created», $Timeout)

    if ($Result.TimedOut -eq $false)
    {
    [System.Windows.Forms.MessageBox]::Show($Result.Name,
    «Появился новый файл», [System.Windows.Forms.MessageBoxButtons]::OK)
    }
    }
    until ([System.Console]::KeyAvailable)

    • 22/08/2020 в 01:22

      Это из-за того, что по умолчанию сборка, отвечающая за создание форм, не погружается в сеанс. Её нужно загрузить вручную командой

      [void][system.reflection.assembly]::
      LoadWithPartialName(«system.windows.Forms»)

      непосредственно перед [System.Windows.Forms.MessageBox]…

      Или вместо того, чтобы выводить месседжбоксы, вывести сообщение, например, через Write-Warning, или же отправить письмо. Тут уж как задача стоит, или на что фантазии хватит 🙂

  7. Рома Карпов
    24/08/2020 в 22:54

    Здорово сработало. Спасибо ВАМ Вот как в готовом варианте
    [void][system.Reflection.Assembly]::LoadWithPartialName(‘System.Windows.Forms’)
    $Path = «C:\Users\ROMAN\Desktop\1»
    $Timeout = 1000
    $Watcher = New-Object System.IO.FileSystemWatcher
    $Watcher.Path = $Path
    $Watcher.Filter = «*.mp3»
    $Result = $Watcher.WaitForChanged(«created», $Timeout)
    do
    {
    $Result = $Watcher.WaitForChanged(«created», $Timeout)
    if ($Result.TimedOut -eq $false)
    {
    [System.Windows.Forms.MessageBox]::Show($Result.Name,
    «Появился новый файл», [System.Windows.Forms.MessageBoxButtons]::OK)
    }
    }
    until ([System.Console]::KeyAvailable)

    Но вопрос,у меня задача чтобы при каждом появлении нового файла в папке появлялись оповещения. А у меня файлы могут появляться друг за другом, надо чтобы окошки так же появлялись друг за другом а то получается пока не закроешь окно, новое оповещение не покажет…И второй вопрос Я думал это можно зациклись в планировщике задач указывая в аргументах файл скрипта. но не выходит задача не выполняется. извините за тупость, Подскажите как это реализовать.

    • 01/09/2020 в 11:15

      А вы уверены, что оповещать нужно именно через окошки? Это жутко не практично.

      Если запускать через планировщик, то должно произойти какое-то событие, при котором сработает триггер. Оно у Вас наступает? Может вы планируете, что скрипт будет запускаться при запуске компьютера, но при этом не перезагружая компьютер ждёте результата?

  1. No trackbacks yet.

Оставьте комментарий