Подписка на события ОС в 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 на экране будет появляться сообщение, информирующее о появлении нового файла:
Я не програмист
Дайте пожалуста целий скрипт.
Или если всьо слепить будет готовий скрипт?
Ответь пожалуста.
Если собрать всё вместе получится скрипт. Только нужно учитывать, что скрипт из статьи будет мониторить появление только mp3-файлов, в каталоге, указанном в переменной $Path. Измените условия под себя.
Добрый день! Спасибо за скрипт. Он отлично работает.
Моя задача следующая:
Появляется новый файл. Закупается сторонний процесс и переносит мой новый файл в подкаталог. Затем снова ждет новый файлик, чтобы повторить процедуру.
Ваш скрипт запоминает документы в папке. срабатывает при появлении нового документа. Но после переноса в подкаталог он прекращает свою работу и больше не ждет ничего нового.
Как мне быть?
Добрый день! Как вариант, Вам нужно зациклить этот код. Думаю, что проще будет оформить его в функцию, и зациклить вызов функции
добрый день, скопировал строки в Мой_файл.ps1, но он не запускается. Куча ошибок- с чем это может быть связано? Ошибки типа- отсутствует скобка, где-то- запятая. почему?
Внимательно посмотрел пример из статьи. Скопировал в редактор — всё ок.
Либо Вы не всё скопировали, либо как вариант у вас такой же интересный глюк, как у меня после последнего обновления системы (уже несколько дней наблюдаю): при вставке текста из буфера в консоль через щелчок правой кнопкой теряются заглавные символы. Т.е., например, вместо строки
$Watcher = New-Object System.IO.FileSystemWatcher
получается
$atcher = ew-bject ystem..ileystematcher
Но при этом вставка через Ctrl+V нормально работает.
Или у Вас какой-то совсем уникальный случай. Если что скиньте саму ошибку, попробуем разобраться вместе.
Спасибо за ответ! я записал в се в 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
Вобщем по-прежнему ничего не работает(
Извините, вот правильная ссылка на ошибку. в предыдущем случае я попытался изменить команду на 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
Подумал, что вам будет удобнее, если я пришлю ссылку
https://drive.google.com/open?id=1ZLJ-htmf_GkurLTy3db6xCKeIa27kxaY
Извините, вот правильная ссылка на ошибку. В предыдущем случае я пробовал изменить команду на 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
Вы запускаете скрипт из оболочки (PS ISE). Сохраните скрипт в файл и запустите из консоли.
Только тогда скорее всего скрипт ругнётся на то, что ничего не знает о формах (System.Windows.Forms), но при этом будет работать. Если Вы не убрали вывод сообщения, нужно подгрузить сборку для форм.
Где-нибудь до вызова формы (и не в цикле) добавьте вот такую строку:
[void][system.reflection.assembly]::LoadWithPartialName(«system.windows.Forms»)
не работает. вы уж меня простите за назойливость
https://drive.google.com/open?id=1l0xexTBS3HaaEwTVN93Tu0VP3GFneYiM
кавычки я поменял, этого текста ошибки больше нет, но.. и программа не работает
Подозрительные у Вас кавычки в 8-й строке, ну ок , а что не работает? На что ругается?
Ошибок нет, но и программа не работает. Вот в таком виде и повисает. После десятка раз контрл с прерывается, в итоге.
https://drive.google.com/open?id=1Xl4sIOJdR_ltg3Td3cTuDsEoId-df9tE
не знаю почему, но вдруг- заработало. Мистика. Спасибо вам за помощь!
Здравствуйте, что то не получается у меня этот скрипт завести, ругается на эту строчку когда в папке добавляю файл с расширением который я указал «Не удалось найти тип [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)
Это из-за того, что по умолчанию сборка, отвечающая за создание форм, не погружается в сеанс. Её нужно загрузить вручную командой
непосредственно перед [System.Windows.Forms.MessageBox]…
Или вместо того, чтобы выводить месседжбоксы, вывести сообщение, например, через Write-Warning, или же отправить письмо. Тут уж как задача стоит, или на что фантазии хватит 🙂
Здорово сработало. Спасибо ВАМ Вот как в готовом варианте
[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)
Но вопрос,у меня задача чтобы при каждом появлении нового файла в папке появлялись оповещения. А у меня файлы могут появляться друг за другом, надо чтобы окошки так же появлялись друг за другом а то получается пока не закроешь окно, новое оповещение не покажет…И второй вопрос Я думал это можно зациклись в планировщике задач указывая в аргументах файл скрипта. но не выходит задача не выполняется. извините за тупость, Подскажите как это реализовать.
А вы уверены, что оповещать нужно именно через окошки? Это жутко не практично.
Если запускать через планировщик, то должно произойти какое-то событие, при котором сработает триггер. Оно у Вас наступает? Может вы планируете, что скрипт будет запускаться при запуске компьютера, но при этом не перезагружая компьютер ждёте результата?