PowerShell。Can';无法从{}获取变量

PowerShell。Can';无法从{}获取变量,powershell,events,filesystemwatcher,Powershell,Events,Filesystemwatcher,在下面的代码中有一个名为“$action”={…}的参数。括号内有一些变量($path、$logPath等)。如何从括号“{}”之外的参数访问变量?我需要我的一个函数(“RunFunctions”)的一个while循环的结果。或者,如果这是一个愚蠢的问题,你能告诉我在哪里可以找到关于这个{}符号的信息吗?谢谢 try { ### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO $watcher = New-Obje

在下面的代码中有一个名为“$action”={…}的参数。括号内有一些变量($path、$logPath等)。如何从括号“{}”之外的参数访问变量?我需要我的一个函数(“RunFunctions”)的一个while循环的结果。或者,如果这是一个愚蠢的问题,你能告诉我在哪里可以找到关于这个{}符号的信息吗?谢谢

try {
    ### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = "C:\Testers\ResetOptimisation\tracking"
    $watcher.Filter = "user_name*.*"
    $watcher.IncludeSubdirectories = $true
    $watcher.EnableRaisingEvents = $true

    ### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
    $action = { $path = $Event.SourceEventArgs.FullPath
                $logPath = "C:\Testers\ResetOptimisation\output\log.txt"
                $changeType = $Event.SourceEventArgs.ChangeType
                $logline = "$(Get-Date), $changeType, $path"
                Add-content $logPath -value $logline
                $terminateFlag = RunFunctions $path $changeType $logPath
            }

    ### DECIDE WHICH EVENTS SHOULD BE WATCHED
    Register-ObjectEvent $watcher "Created" -Action $action
    Register-ObjectEvent $watcher "Changed" -Action $action
    Register-ObjectEvent $watcher "Deleted" -Action $action
    Register-ObjectEvent $watcher "Renamed" -Action $action
    while ($true) {
        Write-Host $teminateFlag
        if ($teminateFlag -eq 1) {
            Exit
        }
        Start-Sleep 3
    }
}
catch {
    Write-Host $_
}
{…}
是一个脚本块(文字)-一段可重用的PowerShell代码,您可以按需执行(就像其他语言中的函数指针或委托)

通过将存储在变量
$action
中的这样一个块传递到
Register ObjectEvent-action
,PowerShell会在感兴趣的事件触发时调用它,并在动态模块中调用,动态模块的作用域与调用方的作用域完全分离

因此,调用代码看不到在块内创建的变量,因为它们是该块的本地变量

有关PowerShell中作用域的更多信息,请参阅的底部部分

虽然PowerShell通常也允许您在其他作用域中创建和修改变量,但如果您采取显式操作,则这不是
Register ObjectEvent-action
的选项,因为动态模块的作用域基本上没有访问调用方作用域的权限,只能访问全局作用域

但是,最好避免使用全局作用域,因为全局变量即使在脚本退出后仍会保留(它们是会话全局变量)

更好的解决方案是:

  • 使动作脚本块为调用者输出一个值

  • 让调用者通过
    receive Job
    ,使用
    的事件作业来接收该输出
    注册ObjectEvent-操作
    返回

下面是一个简化的、自包含的示例,演示了该技术:

它设置一个观察者,附加一个事件处理程序,并创建一个触发观察者事件的文件

try {

  # Specify the target folder: the system's temp folder in this example.
  $dir = (Get-Item -EA Ignore temp:).FullName; if (-not $dir) { $dir = $env:TEMP }

  # Create and initialize the watcher.
  $watcher = [System.IO.FileSystemWatcher] @{
    Filter                = '*.tmp'
    Path                  = $dir
  }

  # Define the action script block (event handler).
  $action = {
    # Print the event data to the host.
    Write-Host "Event raised:`n$($EventArgs | Format-List | Out-String)"
    $terminateFlag = $true
    # *Return* a value that indicates whether watching should be stopped.
    # This value is later retrieved via Receive-Job.
    return $terminateFlag
  }

  # Subscribe to the watcher's Created events, which returns an event job.
  # This indefinitely running job receives the output from the -Action script
  # block whenever the latter is called after an event fires.
  $eventJob = Register-ObjectEvent $watcher Created -Action $action

  # Start watching:
  # Note: Not strictly necessary, because, curiously, 
  #       Register-ObjectEvent has aleady done this for us.
  $watcher.EnableRaisingEvents = $true 

  # Using an aux. background job, create a sample file that will trigger the 
  # watcher, after a delay.
  $tempFile = Join-Path $dir "$PID.tmp"
  $auxJob = Start-Job { Start-Sleep 3; 'hi' > $using:tempFile }

  Write-Host "Watching $dir for creation of $($watcher.Filter) files..."

  # Wait in a loop until the action block is run in response to an event and
  # produces $true as output to signal the intent to terminate, via Receive-Job.
  while ($true -ne (Receive-Job $eventJob)) {
    write-host . -NoNewline
    Start-Sleep -Milliseconds 500  # sleep a little
  }

}
finally {
  # Clean up.
  # Dispose of the watcher.
  $watcher.Dispose() 
  # Remove the event job (and with it the event subscription).
  $eventJob | Remove-Job -Force 
  # Clean up the helper job.
  Remove-Job -ea Ignore -Force $auxJob 
  # Remove the temp. file
  Remove-Item -ea Ignore $tempFile
}

在这段代码中,我的脚本块“$action”能够覆盖在主脚本中初始化的全局参数“terminateFlag”。非常感谢@mklement0和他提供的链接


$script:path=…。等等@RafalRafal:我找到了
$script:
不起作用的原因:事件处理程序在动态模块中运行,而该模块只连接到全局作用域,而不是调用方的。
try {

  # Specify the target folder: the system's temp folder in this example.
  $dir = (Get-Item -EA Ignore temp:).FullName; if (-not $dir) { $dir = $env:TEMP }

  # Create and initialize the watcher.
  $watcher = [System.IO.FileSystemWatcher] @{
    Filter                = '*.tmp'
    Path                  = $dir
  }

  # Define the action script block (event handler).
  $action = {
    # Print the event data to the host.
    Write-Host "Event raised:`n$($EventArgs | Format-List | Out-String)"
    $terminateFlag = $true
    # *Return* a value that indicates whether watching should be stopped.
    # This value is later retrieved via Receive-Job.
    return $terminateFlag
  }

  # Subscribe to the watcher's Created events, which returns an event job.
  # This indefinitely running job receives the output from the -Action script
  # block whenever the latter is called after an event fires.
  $eventJob = Register-ObjectEvent $watcher Created -Action $action

  # Start watching:
  # Note: Not strictly necessary, because, curiously, 
  #       Register-ObjectEvent has aleady done this for us.
  $watcher.EnableRaisingEvents = $true 

  # Using an aux. background job, create a sample file that will trigger the 
  # watcher, after a delay.
  $tempFile = Join-Path $dir "$PID.tmp"
  $auxJob = Start-Job { Start-Sleep 3; 'hi' > $using:tempFile }

  Write-Host "Watching $dir for creation of $($watcher.Filter) files..."

  # Wait in a loop until the action block is run in response to an event and
  # produces $true as output to signal the intent to terminate, via Receive-Job.
  while ($true -ne (Receive-Job $eventJob)) {
    write-host . -NoNewline
    Start-Sleep -Milliseconds 500  # sleep a little
  }

}
finally {
  # Clean up.
  # Dispose of the watcher.
  $watcher.Dispose() 
  # Remove the event job (and with it the event subscription).
  $eventJob | Remove-Job -Force 
  # Clean up the helper job.
  Remove-Job -ea Ignore -Force $auxJob 
  # Remove the temp. file
  Remove-Item -ea Ignore $tempFile
}
try {
    ### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = "C:\Testers\ResetOptimisation\tracking"
    $watcher.Filter = "user_name*.*"
    $watcher.IncludeSubdirectories = $true
    $watcher.EnableRaisingEvents = $true
    $global:terminateFlag = 0

    ### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
    $action = { $path = $Event.SourceEventArgs.FullPath
                $logPath = "C:\Testers\ResetOptimisation\output\log.txt"
                $changeType = $Event.SourceEventArgs.ChangeType
                $logline = "$(Get-Date), $changeType, $path"
                Add-content $logPath -value $logline
                $global:terminateFlag = RunFunctions $path $changeType $logPath
            }

    ### DECIDE WHICH EVENTS SHOULD BE WATCHED
    Register-ObjectEvent $watcher "Created" -Action $action

    while ($true) {
        Write-Host "waiting for file..."
        if ($terminateFlag -eq 1) {
            Exit
        }
        Start-Sleep 10
    }
}
catch {
    Write-Host $_
}