programing

Watch file for changes and run command with powershell

oldcodes 2023. 9. 16. 09:53
반응형

Watch file for changes and run command with powershell

Is there any simple way(i.e., script) to watch file in Powershell and run commands if file changes. I have been googling but can't find simple solution. Basically I run script in Powershell and if file changes then Powershell run other commands.

EDIT

네, 제가 실수를 한 것 같습니다.스크립트는 필요 없습니다. 필요한 기능은 필요 없습니다.$PROFILE.ps1파일. 그래도 열심히 했는데 아직도 못쓰니까 현상금을 주겠습니다.다음과 같이 보여야 합니다.

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

내가 원하는 대로 하는 NPM 모듈이 있습니다.watch, 파일이 아닌 폴더만 감시하고 파워셸 xD는 아닙니다.

Here is an example I have found in my snippets. Hopefully it is a little bit more comprehensive.

First you need to create a file system watcher and subsequently you subscribe to an event that the watcher is generating. This example listens for “Create” events, but could easily be modified to watch out for “Change”.

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

I will try to narrow this down to your requirements.

If you run this as part of your "profile.ps1" script you should read The Power of Profiles which explains the different profile scripts available and more.

Also, you should understand that waiting for a change in a folder can't be run as a function in the script. The profile script has to be finished, for your PowerShell session to start. You can, however use a function to register an event.

What this does, is register a piece of code, to be executed every time an event is triggered. This code will be executed in the context of your current PowerShell host (or shell) while the session remains open. It can interact with the host session, but has no knowledge of the original script that registered the code. The original script has probably finished already, by the time your code is triggered.

Here is the 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 -EventName "Changed" -Action $changeAction
}

 Register-Watcher "c:\temp"

After running this code, change any file in the "C:\temp" directory (or any other directory you specify). You will see an event triggering execution of your code.

Also, valid FileSystemWatcher events you can register are "Changed", "Created", "Deleted" and "Renamed".

이전의 답변이 요구 사항을 충족하지 못했기 때문에 다른 답변을 추가하겠습니다.

요구 사항들

  • 특정 파일의 변경을 대기하는 함수 쓰기
  • 변경이 감지되면 함수는 미리 정의된 명령을 실행하고 실행을 기본 스크립트로 반환합니다.
  • 파일 경로 및 명령은 매개 변수로서 함수에 전달됩니다.

파일 해시를 사용한 답변이 이미 있습니다.이전 답변을 따라 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

여기에 몇 가지 이전 답변을 바탕으로 해결책을 제시합니다.특별히 원했던 것은

  1. 내 코드는 문자열이 아니라 코드가 됩니다.
  2. 콘솔 출력을 볼 수 있도록 I/O 스레드에서 실행할 코드
  3. 변경이 있을 때마다 호출되는 내 코드는 단 한 번도 아닙니다.

사이드 노트: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

좀 더 일반적인 것을 원한다면 폴더/필터/액션을 시계에 전달할 수 있습니다.이것이 다른 사람에게 도움이 되는 출발점이 되기를 바랍니다.

  1. 파일 목록의 해시 계산
  2. 사전에 저장
  3. 간격에 따라 각 해시 검사
  4. 해시가 다를 때 작업 수행

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 "admin@whatever.com" -from "watch@whatever.com" -subject "$($file.Name) has been altered!"'
$f = ls C:\MyFolder\aFile.jpg

watch $f $c 60

를 사용하여 파일을 모니터링할 수 있습니다.

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

이 문서도 참조

여기 또 다른 옵션이 있습니다.

단지 도커 컨테이너 안에서 테스트를 보고 실행하기 위해 나 자신의 것을 적어야 했습니다.Jan의 솔루션은 훨씬 더 우아하지만, File System Watcher는 현재 Docker 컨테이너 내에서 고장이 났습니다.제 접근 방식은 바실리의 접근 방식과 비슷하지만 파일 시스템의 쓰기 시간을 신뢰하는 것이 훨씬 느립니다.

파일이 변경될 때마다 명령 블록을 실행하는 필요한 기능이 여기 있습니다.

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
}

저도 비슷한 문제가 있었습니다.처음에는 Windows 이벤트를 사용하고 등록하려고 했지만, 이 방법은 아래의 해결책처럼 오류에 대한 내성이 적을 것입니다.
제 솔루션은 폴링 스크립트(3초 간격)였습니다.이 스크립트는 시스템에 최소한의 설치 공간을 가지고 있으며 매우 빠르게 변경 사항을 알 수 있습니다.루프를 하는 동안 내 스크립트는 더 많은 작업을 수행할 수 있습니다(실제로 3개의 다른 폴더를 확인합니다).

작업 관리자를 통해 폴링 스크립트가 시작됩니다.일정은 이미 실행 중인 플래그 중지(Stop-When-Running)로 5분마다 시작됩니다.이렇게 하면 재부팅 후 또는 충돌 후 다시 시작됩니다.
3초마다 폴링을 위해 작업 관리자를 사용하는 것은 작업 관리자에게 너무 빈번한 일입니다.스케줄러에 작업을 추가할 때 네트워크 드라이브를 사용하지 않아야 하며(추가 설정이 필요함) 사용자 배치 권한을 부여해야 합니다.

저는 자정 몇 분 전에 스크립트를 종료함으로써 제 스크립트를 깔끔하게 시작합니다.작업 관리자는 매일 아침 스크립트를 시작합니다(스크립트의 init 기능은 자정 1분경에 종료됩니다).

터미널에서 원라이너로 운행할 수 있는 것을 찾고 있었습니다.이것이 제가 도착한 것입니다.

while ($True) { if ((Get-Item .\readme.md).LastWriteTime -ne $LastWriteTime) { "Hello!"; $LastWriteTime = (Get-Item .\readme.md).LastWriteTime; Sleep 1  } }

또 다른 간단한 버전:

$date = get-date
while ( (dir file.txt -ea 0 | % lastwritetime) -lt $date -and $count++ -lt 10) {
  sleep 1
}
'file changed or timeout'

언급URL : https://stackoverflow.com/questions/29066742/watch-file-for-changes-and-run-command-with-powershell

반응형