2015-03-15 10 views
10

Czy istnieje prosty sposób (tzn. Skrypt) do oglądania pliku w trybie powershell i uruchamiania komend, jeśli plik się zmienia. Przeszukałem go, ale nie mogę znaleźć prostego rozwiązania. Zasadniczo uruchamiam skrypt w trybie powershell, a jeśli zmienia się plik, to powershell uruchamia inne polecenia.Obserwuj plik pod kątem zmian i uruchom komendę za pomocą powłoki powershell

EDIT

Ok myślę, że popełnił błąd. Nie potrzebuję skryptu, funkcji potrzeby, którą mogę dołączyć do pliku $PROFILE.ps1. Mimo to mocno trudziłem się i wciąż nie jestem w stanie tego napisać, więc dam nagrodę. To musi wyglądać następująco:

function watch($command, $file) { 
    if($file #changed) { 
    #run $command 
    } 
} 

Jest to moduł npm, że robi to, co chcę, watch, ale to tylko dla zegarków folderach nie plików, a to nie jest PowerShell xD.

+0

Rozszerzyłem moją odpowiedź o rozwiązanie specyficzne dla Twoich wymagań. Czy tego właśnie szukasz? –

Odpowiedz

4

Można użyć monitora System.IO.FileSystemWatcher do monitorowania pliku.

$watcher = New-Object System.IO.FileSystemWatcher 
$watcher.Path = $searchPath 
$watcher.IncludeSubdirectories = $true 
$watcher.EnableRaisingEvents = $true 

Zobacz także this article

+0

Cóż, znajduję to w google, ale jak ja smutno, jak zrobić to w funkcji, która będzie oglądać plik i polecenia ognia? – IGRACH

+0

Spójrz na to [referencje] (http://programmingthisandthat.blogspot.de/2014/04/create-delegates-that-will-be-used-when.html) – bytecode77

+0

Cóż, czytałem to, a ja nie mam pojęcia, co robić. Używam powershell od dłuższego czasu i mam wiele funkcji napisanych, ale to jest takie dezorientujące. – IGRACH

23

Oto przykład znalazłem w moich rozszerzonych. Mam nadzieję, że jest trochę bardziej wszechstronny.

Najpierw należy utworzyć obserwatora systemu plików, a następnie zasubskrybować wydarzenie, które generuje obserwator. W tym przykładzie nasłuchuje zdarzeń "Utwórz", ale można go łatwo zmodyfikować, aby uważać na "Zmień".

$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 
} 

postaram się ograniczyć to w dół do Twoich potrzeb.

Jeśli uruchomisz to jako część skryptu "profile.ps1", powinieneś przeczytać The Power of Profiles, który wyjaśnia różne dostępne skrypty profilów i wiele więcej.

Należy również zrozumieć, że oczekiwanie na zmianę w folderze nie może być uruchamiane jako funkcja w skrypcie. Skrypt profilu musi zostać zakończony, aby rozpocząć sesję PowerShell. Możesz jednak użyć funkcji do zarejestrowania wydarzenia.

Co to jest, rejestruje fragment kodu, który zostanie wykonany za każdym razem, gdy zostanie wywołane zdarzenie. Ten kod zostanie wykonany w kontekście bieżącego hosta PowerShell (lub powłoki), podczas gdy sesja pozostanie otwarta. Może wchodzić w interakcje z sesją hosta, ale nie ma wiedzy o oryginalnym skrypcie, który zarejestrował kod. Oryginalny skrypt prawdopodobnie już się zakończył, do czasu wyzwolenia kodu.

Oto kod:

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" 

Po uruchomieniu tego kodu, zmienić dowolny plik w katalogu „C: \ temp” katalogu (lub dowolny inny katalog określić). Zobaczysz zdarzenie wyzwalające wykonanie Twojego kodu.

Dostępne są również ważne zdarzenia FileSystemWatcher, które można zarejestrować: "Zmieniono", "Utworzono", "Usunięto" i "Zmieniono nazwę".

+0

Hm należy zablokować powłokę podczas działania, tak jak każda funkcja zegarka. – IGRACH

+0

Dość uczciwi, musiałem źle zrozumieć twoje pytanie. Może możesz edytować i dodać to jako wymaganie. Czy też oczekujesz tylko jednej zmiany w pliku lub wielokrotności? –

+0

Świetna odpowiedź, drobny drobiazg @ JanChrbolka, wynik nie ma zamknięcia "w ciągu znaków. (Edycja SO musi być 6 znaków) – Crypth

2

Miałem podobny problem. Najpierw chciałam użyć zdarzeń Windows i zarejestrować się, ale byłoby to mniej odporne na awarie, niż rozwiązanie poniżej.
Moim rozwiązaniem był skrypt odpytywania (interwały 3 sekund).Skrypt ma minimalny ślad w systemie i bardzo szybko zauważa zmiany. Podczas pętli mój skrypt może zrobić więcej rzeczy (w rzeczywistości sprawdzam 3 różne foldery).

Mój skrypt odpytywania jest uruchamiany za pośrednictwem menedżera zadań. Harmonogram rozpoczyna się co 5 minut z zatrzymaniem flagi, kiedy już działa. W ten sposób uruchomi się ponownie po restarcie lub po awarii.
Używanie menedżera zadań do odpytywania co 3 sekundy jest zbyt częste dla menedżera zadań. Po dodaniu zadania do programu planującego, upewnij się, że nie używasz dysków sieciowych (które wymagałyby dodatkowych ustawień) i dawałeś uprawnienia wsadowe dla użytkownika.

Daję skryptowi czysty start, wyłączając go na kilka minut przed północą. Menedżer zadań uruchamia skrypt codziennie rano (funkcja init mojego skryptu zakończy 1 minutę około północy).

+0

Jak jest odpytywanie co 3 sekundy więcej odporności na uszkodzenia, a następnie subskrybowanie zdarzenia, które jest generowane w momencie, gdy następuje zmiana Zależy od wymagań, ale w ciągu 3 sekund możesz pominąć tysiące zdarzeń –

+0

W mojej sytuacji masz kilka plików co minutę. mój program wciąż się uruchamia i jeszcze nie słucha lub restartuje usługę Windows, nie obchodzi mnie to, kiedy podejmujesz środki ostrożności i patrzysz na takie rzeczy jak http://blogs.msdn.com/b/winsdk/archive/2014 /10/29/the-filesystemwatcher-buffer-size.aspx za pomocą zdarzeń również powinien być niezawodny. –

+0

To wystarczająco sprawiedliwe, osobiście uważam, że zdarzenia FileSystemWatcher są bardzo wiarygodne, ponieważ nie wpadłem na b jeszcze ograniczenia uffera. W moim kodzie szukałem małych plików tymczasowych, które pojawiają się tylko ułamek sekundy. –

3
  1. Oblicz skrót listy plików
  2. przechowywać go w słowniku
  3. Sprawdź każdy hash na przedziale
  4. Wykonaj działania podczas mieszania różni

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 
      } 
     } 
    } 
} 

Przykład użycia:

$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 
6

Dodam kolejną odpowiedź, ponieważ moja poprzednia nie spełniła wymagań.

Wymagania

  • Napisz funkcję WAIT o zmianę konkretnego pliku
  • Kiedy zmiana jest wykrywana funkcja wykona predefiniowane polecenie i powrócić wykonanie głównego skryptu
  • Ścieżka do pliku i polecenie są przekazywane do funkcji jako parametry

Istnieje już odpowiedź przy użyciu skrótów plików. Chciałbym śledzić moją poprzednią odpowiedź i pokazać, jak można to osiągnąć za pomocą 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 
2

Oto rozwiązanie, które znalazłem w oparciu o kilka poprzednich odpowiedzi tutaj. I specjalnie chciał:

  1. Mój kod będzie kod, a nie ciąg
  2. Mój kod do uruchomienia na I/O wątku I tak widzę wyjścia konsoli
  3. mojego kodu być wywoływana za każdym razem nastąpiła zmiana, nie raz

marginesie: Zostawiłam w szczegółach to, co chciałem, aby uruchomić ze względu na ironię użyciu zmiennej globalnej do komunikacji pomiędzy wątkami więc mogę skompilować kod 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 

Możesz przesłać folder/filtr/akcja do zegarka, jeśli chcesz czegoś bardziej ogólnego. Mam nadzieję, że jest to przydatny punkt wyjścia dla kogoś innego.

0

Oto kolejna opcja.

Po prostu musiałem napisać własne, aby oglądać i przeprowadzać testy w kontenerze Docker. Rozwiązanie Jana jest znacznie bardziej eleganckie, ale FileSystemWatcher jest obecnie uszkodzony w kontenerach Docker. Moje podejście jest podobne do Vasili, ale znacznie leniwsze, ufające czasowi zapisu systemu plików.

Oto funkcja, której potrzebowałem, która uruchamia blok poleceń za każdym razem, gdy plik się zmienia.

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 
    } 
} 

Oto jeden, który czeka, aż plik się zmieni, uruchomi blok, a następnie wyjdzie.

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 
} 
Powiązane problemy