Vigiar o ficheiro de alterações e executar o comando com o 'powershell'

Existe alguma forma simples (isto é, script) de ver o ficheiro em Powershell e executar comandos se o ficheiro mudar. Tenho pesquisado no Google, mas não encontro uma solução simples. Basicamente eu corro script em Powershell e se o arquivo muda então Powershell executar outros comandos.

editar

Acho que cometi um erro. Eu não preciso de script, uma função necessária que eu possa incluir no meu arquivo $PROFILE.ps1. Mas mesmo assim, estava a esforçar-me e mesmo assim não consigo escrevê-lo, por isso vou dar-lhe recompensa. Tem de ser assim:
function watch($command, $file) {
  if($file #changed) {
    #run $command
  }
}

Existe um módulo NPM que está a fazer o que eu quero, {[[2]} , mas ele só vigia pastas Não ficheiros, e não é Powershell xD.

Author: secondplace, 2015-03-15

7 answers

Aqui está um exemplo que encontrei nos meus excertos. Esperemos que seja um pouco mais abrangente.

Primeiro precisa de criar um observador do sistema de ficheiros e, posteriormente, subscrever um evento que o observador está a gerar. Este exemplo escuta Eventos "Criar", mas pode ser facilmente modificado para ter cuidado com a"mudança".

$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
}
Vou tentar reduzir isto às tuas necessidades.

Se executar isto como parte do seu programa "profile.ps1", deve ler o poder dos perfis o que explica os diferentes scripts de perfis disponíveis e muito mais.

Também, você deve entender que esperar por uma mudança em uma pasta não pode ser executado como uma função no script. O script do perfil tem de ser terminado, para que a sua sessão PowerShell comece. Você pode, no entanto, usar uma função para registrar um evento.

O que isto faz, é registar um pedaço de código, para ser executado sempre que um evento é despoletado. Este código será executado no contexto da sua máquina PowerShell actual (ou shell) enquanto a sessão permanece aberta. Ele pode interagir com a sessão de host, mas não tem conhecimento do script original que registrou o código. O script original já deve ter terminado, quando o seu código for despoletado.

Aqui está o código:

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"

Depois de executar este código, mude QUALQUER ficheiro no "C:\temp" directory (or any other directory you specify). Verá um evento que desencadeia a execução do seu código.

Também, os eventos válidos do FileSystemWatcher que você pode registrar são "alterados", "criados", "excluídos"e " renomeados".

 43
Author: Jan Chrbolka, 2019-08-15 15:50:37
Vou acrescentar outra resposta, porque a minha anterior falhou os requisitos.

Requisitos

  • escreva uma função para espere por uma alteração num ficheiro específico
  • Quando for detectada uma alteração, a função irá executar um comando predefinido e devolver a execução ao programa principal
  • a localização e o comando do Ficheiro são passados à função como parâmetros

Já existe uma resposta usando os traços dos ficheiros. Quero seguir o meu resposta anterior e mostrar-lhe como isso pode ser realizado usando 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
 17
Author: Jan Chrbolka, 2015-04-10 00:16:18

Pode usar o System.IO.FileSystemWatcher para monitorizar um ficheiro.

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

Ver também este artigo

 6
Author: bytecode77, 2015-03-15 22:27:41
Aqui está a solução com a qual acabei baseando-me em várias das respostas anteriores aqui. Eu queria especificamente: [3]}
  1. o meu código para ser Código, não um texto
  2. O meu código a ser executado na linha de E / S para que eu possa ver a saída da consola
  3. o meu código a ser chamado sempre que houve uma mudança, não uma vez
Nota lateral: eu deixei os detalhes do que eu queria executar devido à ironia de usar uma variável global para se comunicar entre threads para que eu possa compilar Erlang codigo.
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

Pode passar na pasta/filtro / acção para vigiar se quiser algo mais genérico. Esperemos que este seja um ponto de partida útil para outra pessoa.

 5
Author: Ed', 2016-06-09 11:28:24
  1. calcular o hash de uma lista de ficheiros
  2. Guarde-o num dicionário
  3. Verifique cada faixa num intervalo
  4. efectuar a acção quando o hash é diferente

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

Uso de exemplo:

$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
 4
Author: Vasili Syrakis, 2015-04-09 12:35:42
Tive um problema semelhante. Eu primeiro queria usar Eventos do Windows e registrar, mas isso seria menos tolerante a falhas como a solução abaixo.
Minha solução foi um script de votação (intervalos de 3 segundos). O script tem uma pegada mínima no sistema e nota mudanças muito rapidamente. Durante o loop meu script pode fazer mais coisas (na verdade, eu verifico 3 pastas diferentes). O meu programa de sondagens é iniciado através do Gestor de Tarefas. O horário começa a cada 5 minutos com o parar a bandeira-quando-já-está-a correr. Desta forma, irá reiniciar após um reiniciamento ou após um estoiro.
Usar o Gerenciador de tarefas para a votação a cada 3 segundos é muito frequente para o Gerenciador de Tarefas. Quando você adicionar uma tarefa ao scheduler certifique-se de que você não usa unidades de rede (que iria pedir configurações extras) e dar os privilégios de lote do seu usuário. Dou um bom começo ao meu guião, fechando-o alguns minutos antes da meia-noite. O Gestor de Tarefas inicia o script todas as manhãs (a função init de my script will exit 1 minute around midnight).
 1
Author: Walter A, 2015-04-04 17:02:17
Aqui está outra opção. Só precisava de escrever a minha para ver e fazer testes dentro de um Contentor. A solução de Jan é muito mais elegante, mas FileSystemWatcher está quebrada dentro de recipientes de Docker atualmente. A minha abordagem é semelhante à do Vasili, mas muito mais preguiçosa, confiando no tempo de escrita do sistema de ficheiros.

Aqui está a função que eu precisava, que executa o bloco de comando cada vez que o arquivo muda.

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
    }
}
Aqui está um que espera até o arquivo mudar, executa o bloqueia, depois sai.
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
}
 1
Author: bjtucker, 2016-09-21 00:47:23