Existe-t-il un moyen simple (script) de regarder un fichier dans powershell et d’exécuter des commandes si le fichier change. J'ai cherché sur Google mais je ne trouve pas de solution simple. Fondamentalement, j'exécute un script dans PowerShell et si les modifications de fichier, PowerShell exécute d'autres commandes.
MODIFIER
Ok je pense avoir commis une erreur. Je n'ai pas besoin de script, une fonction nécessaire que je peux inclure dans mon fichier $PROFILE.ps1
. Mais malgré tout, je discutais avec acharnement et je ne parvenais toujours pas à l'écrire, alors je vais donner une prime. Cela doit ressembler à ceci:
function watch($command, $file) {
if($file #changed) {
#run $command
}
}
Il existe un module npm qui fait ce que je veux, watch
, mais il ne surveille que les dossiers, pas les fichiers, et ce n'est pas PowerShell xD.
Voici un exemple que j'ai trouvé dans mes extraits. J'espère que c'est un peu plus complet.
Tout d'abord, vous devez créer un observateur de système de fichiers, puis vous vous abonnez à un événement généré par l'observateur. Cet exemple écoute les événements «Créer», mais peut facilement être modifié pour surveiller «Changer».
$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
}
Je vais essayer de réduire cela à vos besoins.
Si vous l'exécutez dans le cadre de votre script "profile.ps1", vous devriez lire La puissance des profils qui explique les différents scripts de profil disponibles, etc.
De plus, vous devez comprendre que l'attente d'une modification dans un dossier ne peut pas être exécutée en tant que fonction dans le script. Le script de profil doit être terminé pour que votre session PowerShell puisse démarrer. Vous pouvez cependant utiliser une fonction pour enregistrer un événement.
Cela permet d’enregistrer un morceau de code à exécuter chaque fois qu’un événement est déclenché. Ce code sera exécuté dans le contexte de votre hôte (ou de votre shell) PowerShell actuel tant que la session reste ouverte. Il peut interagir avec la session hôte, mais ne connaît pas le script d'origine qui a enregistré le code. Le script original a probablement déjà fini, au moment où votre code est déclenché.
Voici le 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"
Après avoir exécuté ce code, modifiez tous les fichiers du répertoire "C:\temp" (ou tout autre répertoire de votre choix). Vous verrez un événement déclencher l'exécution de votre code.
En outre, les événements FileSystemWatcher valides que vous pouvez enregistrer sont "Modifié", "Créé", "Supprimé" et "Renommé".
Je vais ajouter une autre réponse, car mon précédent manquait les exigences.
Exigences
Il existe déjà une réponse à l'aide des hachages de fichiers. Je souhaite suivre ma réponse précédente et vous montrer comment cela peut être accompli à l'aide de 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
Vous pouvez utiliser le System.IO.FileSystemWatcher
pour surveiller un fichier.
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $searchPath
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
Voir aussi cet article
Voici la solution que j'ai trouvée avec plusieurs des réponses précédentes ici. Je voulais spécifiquement:
Note latérale: j'ai laissé dans les détails ce que je voulais exécuter en raison de l'ironie d'utiliser une variable globale pour communiquer entre les threads afin de pouvoir compiler le code 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
Vous pouvez passer dans folder/filter/action en watch si vous voulez quelque chose de plus générique. J'espère que c'est un point de départ utile pour quelqu'un d'autre.
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
}
}
}
}
Exemple d'utilisation:
$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
J'avais un problème similaire. Je voulais d’abord utiliser les événements Windows et les enregistrer, mais la solution proposée serait moins tolérante aux pannes.
Ma solution était un script d'interrogation (intervalles de 3 secondes). Le script a une empreinte minimale sur le système et remarque les changements très rapidement. Pendant la boucle, mon script peut faire plus de choses (en fait, je vérifie 3 dossiers différents).
Mon script d'interrogation est lancé via le gestionnaire de tâches. L'horaire commence toutes les 5 minutes avec l'indicateur stop-when-already-déjà en cours d'exécution. De cette façon, il redémarrera après un redémarrage ou après un crash.
Utiliser le gestionnaire de tâches pour une interrogation toutes les 3 secondes est trop fréquent pour le gestionnaire de tâches . Lorsque vous ajoutez une tâche au planificateur, assurez-vous de ne pas utiliser de lecteurs réseau privilèges de lot utilisateur.
Je donne à mon script un bon départ en l'éteignant quelques minutes avant minuit. Le gestionnaire de tâches lance le script tous les matins (la fonction init de mon script quitte 1 minute environ vers minuit).
Voici une autre option.
Je devais juste écrire le mien pour pouvoir regarder et exécuter des tests dans un conteneur Docker. La solution de Jan est beaucoup plus élégante, mais FileSystemWatcher est actuellement cassé dans les conteneurs Docker. Mon approche est similaire à celle de Vasili, mais beaucoup plus paresseuse, faisant confiance au temps d'écriture du système de fichiers.
Voici la fonction dont j'avais besoin, qui exécute le bloc de commandes chaque fois que le fichier est modifié.
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
}
}
En voici une qui attend que le fichier soit modifié, exécute le bloc, puis se ferme.
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
}