Powershell - 如何将变量传递到脚本块
Powershell - How to pass variable into scriptblock
我正在尝试了解并弄清楚如何将变量传递到脚本块中。在我下面的示例脚本中,当一个新文件被放入受监视的文件夹中时,它会执行 $action 脚本块。但是 $test1 变量只是显示为空白。使它工作的唯一方法是将其设为全局变量,但我真的不想那样做。
我已经对此进行了一些研究,但比开始时更加困惑。任何人都可以帮助我或指出正确的方向来理解这一点吗?
$PathToMonitor = "\path\to\folder"
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path = $PathToMonitor
$FileSystemWatcher.Filter = "*.*"
$FileSystemWatcher.IncludeSubdirectories = $false
$FileSystemWatcher.EnableRaisingEvents = $true
$test1 = "Test variable"
$Action = {
Write-Host "$test1"
}
$handlers = . {
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $Action -SourceIdentifier FSCreateConsumer
}
try {
do {
Wait-Event -Timeout 5
} while ($true)
}
finally {
Unregister-Event -SourceIdentifier FSCreateConsumer
$handlers | Remove-Job
$FileSystemWatcher.EnableRaisingEvents = $false
$FileSystemWatcher.Dispose()
}
事件操作块在后台线程上运行,无法在调度时解析 $test1
。
一种解决方法是显式读取和写入全局范围的变量(例如 Write-Host $global:test1
),但更好的解决方案是确保 $Action
块“记住”的值$test1
稍后 - 我们可以用 a closure.
来完成
我们需要为此稍微重组代码,所以首先用同步哈希表替换 $test1
字符串文字:
$test1 = [hashtable]::Synchronized(@{
Value = "Test variable"
})
这将使我们能够做两件事:
- 我们可以修改字符串值而不改变存储在
$test1
, 中的对象的身份
- 字符串值可以被多个后台线程修改而不会出现任何竞争条件
现在我们只需要从 $Action
块创建闭包:
$Action = {
Write-Host $test1.Value
}.GetNewClosure()
这会将 $test1
的值(对我们刚刚在上面一行创建的同步哈希表的引用)绑定到 $Action
块,因此它将“记住” $test1
解析为哈希表,而不是尝试(并失败)在运行时解析它。
隐藏在 Register-ObjectEvent, way down in the -Action
参数描述的文档中的是这个小花絮:
The value of the Action parameter can include the $Event, $EventSubscriber, $Sender, $EventArgs, and $Args automatic variables. These variables provide information about the event to the Action script block. For more information, see about_Automatic_Variables.
这意味着 PowerShell 会自动创建一些您可以在事件处理程序脚本块中使用的变量,并在触发事件时填充它们 - 例如:
$Action = {
write-host ($Sender | format-list * | out-string)
write-host ($EventArgs | format-list * | out-string)
}
当您在监视文件夹中创建文件时,您会看到如下输出:
NotifyFilter : FileName, DirectoryName, LastWrite
Filters : {*}
EnableRaisingEvents : True
Filter : *
IncludeSubdirectories : False
InternalBufferSize : 8192
Path : c:\temp\scratch
Site :
SynchronizingObject :
Container :
ChangeType : Created
FullPath : c:\temp\scratch\New Text Document (3).txt
Name : New Text Document (3).txt
如果这些包含您想要的信息,那么您实际上不需要自己将任何参数传递到脚本块中:-)。
更新
如果您仍然需要将自己的变量传递到事件中,您可以使用 Register-ObjectEvent
的 -MessageData
参数,以便能够在事件脚本块中以 $Event.MessageData
的形式访问它 -例如:
$Action = {
write-host ($EventArgs | format-list * | out-string)
write-host "messagedata before = "
write-host ($Event.MessageData | ConvertTo-Json)
$Event.MessageData.Add($EventArgs.FullPath, $true)
write-host "messagedata after = "
write-host ($Event.MessageData | ConvertTo-Json)
}
$messageData = @{ };
$handlers = . {
# note the -MessageData parameter
Register-ObjectEvent `
-InputObject $FileSystemWatcher `
-EventName Created `
-Action $Action `
-MessageData $messageData `
-SourceIdentifier FSCreateConsumer
}
事件触发时会输出如下内容:
ChangeType : Created
FullPath : c:\temp\scratch\New Text Document (16).txt
Name : New Text Document (16).txt
messagedata before =
{}
messagedata after =
{
"c:\temp\scratch\New Text Document (16).txt": true
}
$messageData
在技术上仍然是一个全局变量,但您的 $Action
不再需要知道它,因为它从 $Event
.
中获取引用
请注意,如果您想保留更改,您需要使用 mutable 数据结构 - 您不能只为 $Event.MessageData
分配一个新值,并且它可能也需要线程安全。
我正在尝试了解并弄清楚如何将变量传递到脚本块中。在我下面的示例脚本中,当一个新文件被放入受监视的文件夹中时,它会执行 $action 脚本块。但是 $test1 变量只是显示为空白。使它工作的唯一方法是将其设为全局变量,但我真的不想那样做。
我已经对此进行了一些研究,但比开始时更加困惑。任何人都可以帮助我或指出正确的方向来理解这一点吗?
$PathToMonitor = "\path\to\folder"
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path = $PathToMonitor
$FileSystemWatcher.Filter = "*.*"
$FileSystemWatcher.IncludeSubdirectories = $false
$FileSystemWatcher.EnableRaisingEvents = $true
$test1 = "Test variable"
$Action = {
Write-Host "$test1"
}
$handlers = . {
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $Action -SourceIdentifier FSCreateConsumer
}
try {
do {
Wait-Event -Timeout 5
} while ($true)
}
finally {
Unregister-Event -SourceIdentifier FSCreateConsumer
$handlers | Remove-Job
$FileSystemWatcher.EnableRaisingEvents = $false
$FileSystemWatcher.Dispose()
}
事件操作块在后台线程上运行,无法在调度时解析 $test1
。
一种解决方法是显式读取和写入全局范围的变量(例如 Write-Host $global:test1
),但更好的解决方案是确保 $Action
块“记住”的值$test1
稍后 - 我们可以用 a closure.
我们需要为此稍微重组代码,所以首先用同步哈希表替换 $test1
字符串文字:
$test1 = [hashtable]::Synchronized(@{
Value = "Test variable"
})
这将使我们能够做两件事:
- 我们可以修改字符串值而不改变存储在
$test1
, 中的对象的身份
- 字符串值可以被多个后台线程修改而不会出现任何竞争条件
现在我们只需要从 $Action
块创建闭包:
$Action = {
Write-Host $test1.Value
}.GetNewClosure()
这会将 $test1
的值(对我们刚刚在上面一行创建的同步哈希表的引用)绑定到 $Action
块,因此它将“记住” $test1
解析为哈希表,而不是尝试(并失败)在运行时解析它。
隐藏在 Register-ObjectEvent, way down in the -Action
参数描述的文档中的是这个小花絮:
The value of the Action parameter can include the $Event, $EventSubscriber, $Sender, $EventArgs, and $Args automatic variables. These variables provide information about the event to the Action script block. For more information, see about_Automatic_Variables.
这意味着 PowerShell 会自动创建一些您可以在事件处理程序脚本块中使用的变量,并在触发事件时填充它们 - 例如:
$Action = {
write-host ($Sender | format-list * | out-string)
write-host ($EventArgs | format-list * | out-string)
}
当您在监视文件夹中创建文件时,您会看到如下输出:
NotifyFilter : FileName, DirectoryName, LastWrite
Filters : {*}
EnableRaisingEvents : True
Filter : *
IncludeSubdirectories : False
InternalBufferSize : 8192
Path : c:\temp\scratch
Site :
SynchronizingObject :
Container :
ChangeType : Created
FullPath : c:\temp\scratch\New Text Document (3).txt
Name : New Text Document (3).txt
如果这些包含您想要的信息,那么您实际上不需要自己将任何参数传递到脚本块中:-)。
更新
如果您仍然需要将自己的变量传递到事件中,您可以使用 Register-ObjectEvent
的 -MessageData
参数,以便能够在事件脚本块中以 $Event.MessageData
的形式访问它 -例如:
$Action = {
write-host ($EventArgs | format-list * | out-string)
write-host "messagedata before = "
write-host ($Event.MessageData | ConvertTo-Json)
$Event.MessageData.Add($EventArgs.FullPath, $true)
write-host "messagedata after = "
write-host ($Event.MessageData | ConvertTo-Json)
}
$messageData = @{ };
$handlers = . {
# note the -MessageData parameter
Register-ObjectEvent `
-InputObject $FileSystemWatcher `
-EventName Created `
-Action $Action `
-MessageData $messageData `
-SourceIdentifier FSCreateConsumer
}
事件触发时会输出如下内容:
ChangeType : Created
FullPath : c:\temp\scratch\New Text Document (16).txt
Name : New Text Document (16).txt
messagedata before =
{}
messagedata after =
{
"c:\temp\scratch\New Text Document (16).txt": true
}
$messageData
在技术上仍然是一个全局变量,但您的 $Action
不再需要知道它,因为它从 $Event
.
请注意,如果您想保留更改,您需要使用 mutable 数据结构 - 您不能只为 $Event.MessageData
分配一个新值,并且它可能也需要线程安全。