PowerShellでファイルを監視し、ファイルが変更された場合にコマンドを実行する簡単な方法(スクリプトなど)はありますか私はグーグルで検索してきましたが、簡単な解決策が見つかりません。基本的に私はpowershellでスクリプトを実行し、ファイルが変更された場合、powershellは他のコマンドを実行します。
編集
間違いだと思います。スクリプトは必要ありません。$PROFILE.ps1
ファイルに含めることができる関数が必要です。しかし、それでも、私は懸命にツルーイングをしていましたが、それでも書くことができません。次のようになります。
function watch($command, $file) {
if($file #changed) {
#run $command
}
}
watch
、私がしたいことをしているnpmモジュールがありますが、それはファイルではなくフォルダを監視するだけで、PowerShell xDではありません。
これがスニペットで見つかった例です。少しでも包括的であることを願っています。
最初にファイルシステムウォッチャーを作成し、その後ウォッチャーが生成するイベントにサブスクライブする必要があります。この例は「作成」イベントをリッスンしますが、「変更」に注意するように簡単に変更できます。
$folder = "C:\Users\LOCAL_~1\AppData\Local\Temp\3"
$filter = "*.LOG"
$Watcher = New-Object IO.FileSystemWatcher $folder, $filter -Property @{
IncludeSubdirectories = $false
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
$onCreated = Register-ObjectEvent $Watcher -EventName Created -SourceIdentifier FileCreated -Action {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Host "The file '$name' was $changeType at $timeStamp"
Write-Host $path
#Move-Item $path -Destination $destination -Force -Verbose
}
これをお客様の要件に絞り込みます。
これを「profile.ps1」スクリプトの一部として実行する場合、 The Power of Profiles を読む必要があります。これは、使用可能なさまざまなプロファイルスクリプトなどを説明しています。
また、フォルダー内の変更を待機することは、スクリプト内の関数として実行できないことを理解する必要があります。 PowerShellセッションを開始するには、プロファイルスクリプトを終了する必要があります。ただし、関数を使用してイベントを登録できます。
これが行うことは、イベントがトリガーされるたびに実行されるコードを登録することです。このコードは、セッションが開いている間、現在のPowerShellホスト(またはシェル)のコンテキストで実行されます。ホストセッションと対話できますが、コードを登録した元のスクリプトの知識はありません。コードがトリガーされるまでに、元のスクリプトはおそらく既に終了しています。
コードは次のとおりです。
Function Register-Watcher {
param ($folder)
$filter = "*.*" #all files
$watcher = New-Object IO.FileSystemWatcher $folder, $filter -Property @{
IncludeSubdirectories = $false
EnableRaisingEvents = $true
}
$changeAction = [scriptblock]::Create('
# This is the code which will be executed every time a file change is detected
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Host "The file $name was $changeType at $timeStamp"
')
Register-ObjectEvent $Watcher -EventName "Changed" -Action $changeAction
}
Register-Watcher "c:\temp"
このコードを実行した後、「C:\ temp」ディレクトリ(または指定した他のディレクトリ)のファイルを変更します。コードの実行をトリガーするイベントが表示されます。
また、登録できる有効なFileSystemWatcherイベントは、「変更」、「作成」、「削除」、および「名前変更」です。
以前の回答が要件を満たしていなかったため、別の回答を追加します。
要件
ファイルハッシュを使用した回答が既にあります。以前の回答に従い、FileSystemWatcherを使用してこれを実現する方法を示します。
$File = "C:\temp\log.txt"
$Action = 'Write-Output "The watched file was changed"'
$global:FileChanged = $false
function Wait-FileChange {
param(
[string]$File,
[string]$Action
)
$FilePath = Split-Path $File -Parent
$FileName = Split-Path $File -Leaf
$ScriptBlock = [scriptblock]::Create($Action)
$Watcher = New-Object IO.FileSystemWatcher $FilePath, $FileName -Property @{
IncludeSubdirectories = $false
EnableRaisingEvents = $true
}
$onChange = Register-ObjectEvent $Watcher Changed -Action {$global:FileChanged = $true}
while ($global:FileChanged -eq $false){
Start-Sleep -Milliseconds 100
}
& $ScriptBlock
Unregister-Event -SubscriptionId $onChange.Id
}
Wait-FileChange -File $File -Action $Action
System.IO.FileSystemWatcher
ファイルを監視します。
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $searchPath
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
この記事 も参照してください
ここに、以前のいくつかの回答に基づいて私が結んだ解決策があります。私は特に欲しかった:
サイドノート:グローバル変数を使用してスレッド間で通信し、Erlangコードをコンパイルできるという皮肉なことから、実行したいことの詳細を残しました。
Function RunMyStuff {
# this is the bit we want to happen when the file changes
Clear-Host # remove previous console output
& 'C:\Program Files\erl7.3\bin\erlc.exe' 'program.erl' # compile some erlang
erl -noshell -s program start -s init stop # run the compiled erlang program:start()
}
Function Watch {
$global:FileChanged = $false # dirty... any better suggestions?
$folder = "M:\dev\Erlang"
$filter = "*.erl"
$watcher = New-Object IO.FileSystemWatcher $folder, $filter -Property @{
IncludeSubdirectories = $false
EnableRaisingEvents = $true
}
Register-ObjectEvent $Watcher "Changed" -Action {$global:FileChanged = $true} > $null
while ($true){
while ($global:FileChanged -eq $false){
# We need this to block the IO thread until there is something to run
# so the script doesn't finish. If we call the action directly from
# the event it won't be able to write to the console
Start-Sleep -Milliseconds 100
}
# a file has changed, run our stuff on the I/O thread so we can see the output
RunMyStuff
# reset and go again
$global:FileChanged = $false
}
}
RunMyStuff # run the action at the start so I can see the current output
Watch
より一般的なものが必要な場合は、folder/filter/actionをwatchに渡すことができます。うまくいけば、これは他の誰かにとって役立つ出発点になるでしょう。
function watch($f, $command, $interval) {
$sha1 = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider
$hashfunction = '[System.BitConverter]::ToString($sha1.ComputeHash([System.IO.File]::ReadAllBytes($file)))'
$files = @{}
foreach ($file in $f) {
$hash = iex $hashfunction
$files[$file.Name] = $hash
echo "$hash`t$($file.FullName)"
}
while ($true) {
sleep $interval
foreach ($file in $f) {
$hash = iex $hashfunction
if ($files[$file.Name] -ne $hash) {
iex $command
}
}
}
}
使用例:
$c = 'send-mailmessage -to "[email protected]" -from "[email protected]" -subject "$($file.Name) has been altered!"'
$f = ls C:\MyFolder\aFile.jpg
watch $f $c 60
同様の問題がありました。私は最初にWindowsイベントを使用して登録したかったのですが、これはその下のソリューションほどフォールトトレラントではありませんでした。
私の解決策は、ポーリングスクリプト(3秒間隔)でした。このスクリプトは、システム上のフットプリントが最小限であり、変更を非常にすばやく認識します。ループ中に、スクリプトはもっと多くのことができます(実際には3つの異なるフォルダーをチェックします)。
ポーリングスクリプトは、タスクマネージャーを介して開始されます。スケジュールは5分ごとに開始され、フラグはすでに実行されているときに停止します。これにより、再起動後またはクラッシュ後に再起動します。
3秒ごとにポーリングするためにタスクマネージャーを使用することは、タスクマネージャーにとってあまりにも頻繁です。スケジューラにタスクを追加するときは、ネットワークドライブ(追加の設定が必要)を使用しないようにし、ユーザーにバッチ特権を付与します。
真夜中の数分前にスクリプトをシャットダウンすることで、スクリプトをクリーンに開始します。タスクマネージャーはスクリプトを毎朝開始します(スクリプトのinit関数は真夜中の1分後に終了します)。
別のオプションがあります。
Dockerコンテナー内でテストを監視および実行するために自分で作成する必要がありました。 Janのソリューションははるかにエレガントですが、FileSystemWatcherは現在Dockerコンテナー内で壊れています。私のアプローチはVasiliのアプローチに似ていますが、ファイルシステムの書き込み時間を信頼する、はるかに怠lazです。
ファイルが変更されるたびにコマンドブロックを実行する必要な関数を次に示します。
function watch($command, $file) {
$this_time = (get-item $file).LastWriteTime
$last_time = $this_time
while($true) {
if ($last_time -ne $this_time) {
$last_time = $this_time
invoke-command $command
}
sleep 1
$this_time = (get-item $file).LastWriteTime
}
}
ファイルが変更されるまで待機し、ブロックを実行してから終了するものを次に示します。
function waitfor($command, $file) {
$this_time = (get-item $file).LastWriteTime
$last_time = $this_time
while($last_time -eq $this_time) {
sleep 1
$this_time = (get-item $file).LastWriteTime
}
invoke-command $command
}