监视文件的更改并使用powershell运行命令

mu0hgdu0  于 2022-12-23  发布在  Shell
关注(0)|答案(9)|浏览(164)

有没有简单的方法(即脚本)来观看文件在Powershell和运行命令,如果文件发生变化。我一直在谷歌搜索,但不能找到简单的解决方案。基本上我运行脚本在Powershell和如果文件发生变化,然后Powershell运行其他命令。
编辑
好吧,我想我犯了一个错误。我不需要脚本,一个需要的函数,我可以包括在我的$PROFILE.ps1文件。但仍然,我很努力,仍然我不能写它,所以我会给予赏金。它必须看起来像这样:

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

有一个NPM模块正在做我想做的事情,watch,但它只监视文件夹而不是文件,而且它不是Powershell xD。

fkvaft9z

fkvaft9z1#

这里是我在我的片段中找到的一个例子。希望它更全面一点。
首先,您需要创建一个文件系统监视器,然后订阅该监视器生成的事件。此示例侦听"Create"事件,但可以轻松地修改为监视"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
}

我会尽量缩小范围,满足你的要求.
如果您将此作为"profile.ps1"脚本的一部分运行,您应该阅读The Power of Profiles,其中解释了可用的不同配置文件脚本等。
此外,您应该了解,等待文件夹中的更改不能作为脚本中的函数运行。配置文件脚本必须完成,才能启动PowerShell会话。但是,您可以使用函数注册事件。
它的作用是注册一段代码,在每次触发事件时执行。当会话保持打开时,此代码将在当前PowerShell主机(或shell)的上下文中执行。它可以与主机会话交互,但不知道注册代码的原始脚本。在触发代码时,原始脚本可能已经完成。
下面是代码:

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"

运行完这段代码后,更改"C:\temp"目录(或您指定的任何其他目录)中的任何文件,您将看到一个触发代码执行的事件。
此外,您可以注册的有效FileSystemWatcher事件包括"已更改"、"已创建"、"已删除"和"已重命名"。

yqkkidmi

yqkkidmi2#

我将添加另一个答案,因为我的前一个确实错过了要求。

要求

  • 编写一个函数到WAIT以获取特定文件中的更改
  • 当检测到更改时,函数将执行预定义的命令并将执行返回到主脚本
  • 文件路径和命令作为参数传递给函数

已经有一个使用文件散列的答案了。我想按照我之前的答案,向您展示如何使用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
zengzsys

zengzsys3#

以下是我根据前面几个答案得出的解决方案。我特别希望:
1.我的代码是代码,而不是字符串
1.我的代码将在I/O线程上运行,这样我就可以看到控制台输出
1.每次发生更改时都会调用我的代码,而不是一次
边注:我已经留下了我想要运行的细节,因为使用全局变量在线程之间通信是一个讽刺,这样我就可以编译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

如果你想要更通用的东西,你可以把folder/filter/action传递到watch中。希望这对其他人来说是一个有帮助的起点。

zbq4xfa0

zbq4xfa04#

1.计算文件列表的哈希
1.将其存储在字典中
1.按时间间隔检查每个哈希
1.哈希不同时执行操作

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
xesrikrc

xesrikrc5#

您可以使用System.IO.FileSystemWatcher监视文件。

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

另请参见this article

uklbhaso

uklbhaso6#

这是另一个选择。
我只需要编写自己的代码来在Docker容器中监视和运行测试。Jan的解决方案要优雅得多,但FileSystemWatcher目前在Docker容器中是坏的。我的方法与Vasili的类似,但更懒,相信文件系统的写入时间。
下面是我需要的函数,它在每次文件更改时运行命令块。

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

q0qdq0h27#

我也遇到过类似的问题,我最初想使用Windows事件和注册,但这会比下面的解决方案容错性差。
我的解决方案是一个轮询脚本(间隔3秒)。该脚本占用系统的空间最小,并且可以非常快地注意到变化。在循环过程中,我的脚本可以做更多的事情(实际上我检查了3个不同的文件夹)。
我的轮询脚本是通过任务管理器启动的。计划每5分钟启动一次,并带有stop-when-already-running标志。这样,它将在重新启动或崩溃后重新启动。
使用任务管理器每3秒轮询一次对任务管理器来说太频繁了。当您将任务添加到调度程序时,请确保您没有使用网络驱动器(这将需要额外的设置),并给予您的用户批处理权限。
我在午夜前几分钟关闭脚本,让它有一个干净的开始,任务管理器每天早上启动脚本(脚本的init函数在午夜前后1分钟退出)。

ha5z0ras

ha5z0ras8#

我在寻找一种可以从航站楼搭乘一班飞机的东西。这是我找到的东西:

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

7vhp5slm9#

另一个简单的版本:

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

相关问题