Gibt es eine einfache Möglichkeit (z. B. Skript), um die Datei in Powershell zu überwachen und Befehle auszuführen, wenn sich die Datei ändert. Ich habe gegoogelt, aber keine einfache Lösung gefunden. Grundsätzlich führe ich ein Skript in Powershell aus und wenn Datei geändert wird, führt Powershell andere Befehle aus.
BEARBEITEN
Ok, ich glaube, ich habe einen Fehler gemacht. Ich brauche kein Skript, eine Bedarfsfunktion, die ich in meine $PROFILE.ps1
-Datei einfügen kann. Trotzdem war ich hart und konnte immer noch nicht schreiben, also gebe ich Kopfgeld. Es muss so aussehen:
function watch($command, $file) {
if($file #changed) {
#run $command
}
}
Es gibt ein npm-Modul, das tut was ich will, watch
, aber es überwacht nur Ordner, keine Dateien, und es ist keine Powershell-xD.
Hier ist ein Beispiel, das ich in meinen Ausschnitten gefunden habe. Hoffentlich ist es etwas umfassender.
Zuerst müssen Sie einen Dateisystem-Watcher erstellen. Anschließend abonnieren Sie ein Ereignis, das der Watcher generiert. Dieses Beispiel wartet auf "Erstellen" -Ereignisse, kann jedoch leicht geändert werden, um auf "Ändern" zu achten.
$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 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
}
Ich werde versuchen, dies auf Ihre Anforderungen einzugrenzen.
Wenn Sie dies als Teil Ihres "profile.ps1" -Skripts ausführen, sollten Sie The Power of Profiles lesen, in dem die verschiedenen verfügbaren Profilskripts erläutert werden.
Sie sollten auch verstehen, dass das Warten auf eine Änderung in einem Ordner nicht als Funktion im Skript ausgeführt werden kann. Das Profilskript muss abgeschlossen sein, damit Ihre PowerShell-Sitzung gestartet werden kann. Sie können jedoch eine Funktion verwenden, um ein Ereignis zu registrieren.
Was dies tut, ist das Registrieren eines Codeabschnitts, der jedes Mal ausgeführt wird, wenn ein Ereignis ausgelöst wird. Dieser Code wird im Kontext Ihres aktuellen PowerShell-Hosts (oder der aktuellen Shell) ausgeführt, während die Sitzung geöffnet bleibt. Es kann mit der Host-Sitzung interagieren, hat jedoch keine Kenntnis des ursprünglichen Skripts, das den Code registriert hat. Das Originalskript ist wahrscheinlich bereits fertig, wenn Ihr Code ausgelöst wird.
Hier ist der Code:
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 "Changed" -Action $changeAction
}
Register-Watcher "c:\temp"
Ändern Sie nach dem Ausführen dieses Codes alle Dateien im Verzeichnis "C:\temp" (oder in einem anderen von Ihnen angegebenen Verzeichnis). Sie sehen ein Ereignis, das die Ausführung Ihres Codes auslöst.
Gültige FileSystemWatcher-Ereignisse, die Sie registrieren können, sind "Geändert", "Erstellt", "Gelöscht" und "Umbenannt".
Ich werde noch eine Antwort hinzufügen, da meine vorherige die Anforderungen nicht erfüllt hat.
Bedarf
Es gibt bereits eine Antwort mit Dateihashes. Ich möchte meiner vorherigen Antwort folgen und Ihnen zeigen, wie dies mit FileSystemWatcher erreicht werden kann.
$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
Sie können den System.IO.FileSystemWatcher
verwenden, um eine Datei zu überwachen.
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $searchPath
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
Siehe auch diesen Artikel
Hier ist die Lösung, die ich mit einigen der vorherigen Antworten gefunden habe. Ich wollte speziell:
Randbemerkung: Ich habe in den Details dessen, was ich ausführen wollte, aufgrund der Ironie der Verwendung einer globalen Variablen für die Kommunikation zwischen Threads übrig gelassen, damit ich Erlang-Code kompilieren kann.
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
Sie können Ordner/Filter/Aktion in watch übergeben, wenn Sie etwas mehr generisches wollen. Hoffentlich ist dies ein hilfreicher Ausgangspunkt für jemanden.
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
}
}
}
}
Verwendungsbeispiel:
$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
Ich hatte ein ähnliches Problem. Ich wollte zuerst Windows-Ereignisse verwenden und registrieren, dies wäre jedoch weniger fehlertolerant als die Lösung darunter.
Meine Lösung war ein Abfrageskript (Intervalle von 3 Sekunden). Das Skript hat einen minimalen Footprint im System und bemerkt Änderungen sehr schnell. Während der Schleife kann mein Skript mehr tun (eigentlich überprüfe ich 3 verschiedene Ordner).
Mein Abfrageskript wird über den Task-Manager gestartet. Der Zeitplan wird alle 5 Minuten mit der Markierung angehalten, wenn bereits läuft. Auf diese Weise wird es nach einem Neustart oder nach einem Absturz neu gestartet.
Die Verwendung des Task-Managers zum Abfragen alle 3 Sekunden ist für den Task-Manager zu häufig. Wenn Sie dem Scheduler eine Aufgabe hinzufügen, stellen Sie sicher, dass Sie keine Netzlaufwerke verwenden (für die zusätzliche Einstellungen erforderlich sind), und geben Sie Ihren Task an Benutzerbatch-Berechtigungen.
Ich gebe meinem Skript einen sauberen Start, indem ich es einige Minuten vor Mitternacht herunterfahre. Der Task-Manager startet das Skript jeden Morgen (die Init-Funktion meines Skripts wird etwa eine Minute um Mitternacht beendet).
Hier ist eine weitere Option.
Ich musste nur meine eigenen schreiben, um Tests in einem Docker-Container zu sehen und auszuführen. Die Lösung von Jan ist viel eleganter, aber FileSystemWatcher ist derzeit in Docker-Containern defekt. Mein Ansatz ist dem von Vasili ähnlich, aber er ist viel fauler und vertraut der Schreibzeit des Dateisystems.
Hier ist die Funktion, die ich brauchte, die den Befehlsblock jedes Mal ausführt, wenn sich die Datei ändert.
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
}
}
Hier ist einer, der wartet, bis sich die Datei ändert, den Block ausführt und dann beendet wird.
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
}