在 PowerShell 中从另一个函数调用一个函数
Calling a Function From Another Function in PowerShell
第一次使用 PowerShell 5,我在调用一个将消息写入文件的函数时遇到了问题。以下是我正在做的简化版本。
workflow test {
function logMessage {
param([string] $Msg)
Write-Output $Msg
}
function RemoveMachineFromCollection{
param([string]$Collection, [string]$Machine)
# If there's an error
LogMessage "Error Removing Machine"
# If all is good
LogMessage "successfully remove machine"
}
$Collections = DatabaseQuery1
foreach -parallel($coll in $Collections) {
logMessage "operating on $coll collection"
$Machines = DatabaseQuery2
foreach($Mach in $Machines) {
logMessage "Removing $Mach from $coll"
RemoveMachineFromCollection -Collection $coll -Machine $Mach
}
}
}
test
这是它生成的错误:
The term 'logMessage' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
+ CategoryInfo : ObjectNotFound: (logMessage:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : [localhost]
我试过在文件中四处移动 logMessage 函数,甚至尝试过全局范围。
在任何其他语言中,我都可以从任何其他函数调用 logMessage。因为这就是函数的目的。
重用代码块的"Workflow way"是什么?
我是否需要创建一些加载到工作流中的日志记录模块?
Powershell 要求在使用前定义函数 ('lexical scope')。在您的示例中,您在定义 logMessage
函数之前调用它。
您还将示例构建为 Powershell 工作流。工作流有一些普通脚本没有的限制;您需要了解这些差异。我确实this search to find some descriptions and discussions of the differences; the first "hit"提供了很好的资料。我(还)没有发现任何关于函数是否可以在工作流中定义的说法,但我会非常谨慎地首先在函数(或工作流)中定义函数。
您可以将函数和函数调用移动到工作流中的 InlineScript
(PowerShell ScriptBlock),如下所示。
workflow test {
InlineScript
{
function func1{
Write-Output "Func 1"
logMessage
}
function logMessage{
Write-Output "logMessage"
}
func1
}
}
将输出:
Func 1
logMessage
正如@JeffZeitlin 在他的回答中提到的,工作流不是 PowerShell,而且限制更多。 InlineScript 块允许解释普通的 PowerShell 代码,但是范围将绑定到 InlineScript 块。例如,如果您在脚本块中定义函数,然后尝试调用 InlineScript 块外的 func1
函数(但仍在工作流中),它将失败,因为它超出了范围。
如果您在工作流外部或工作流内部但不在 InlineScript 块中定义这两个函数,也会发生同样的情况。
现在举例说明如何将此应用到 运行 一个 foreach -parallel
循环。
workflow test {
## workflow parameter
param($MyList)
## parallel foreach loop on workflow parameter
foreach -parallel ($Item in $MyList)
{
## inlinescript
inlinescript
{
## function func1 declaration
function func1{
param($MyItem)
Write-Output ('Func 1, MyItem {0}' -f $MyItem)
logMessage $MyItem
}
## function logMessage declaration
function logMessage{
param($MyItem)
Write-Output ('logMessage, MyItem: {0}' -f $MyItem)
}
## func1 call with $Using:Item statement
## $Using: prefix allows us to call items that are in the workflow scope but not in the inlinescript scope.
func1 $Using:Item
}
}
}
对此工作流的调用示例如下所示
PS> $MyList = 1,2,3
PS> test $MyList
Func 1, MyItem 3
Func 1, MyItem 1
Func 1, MyItem 2
logMessage, MyItem: 3
logMessage, MyItem: 2
logMessage, MyItem: 1
您会注意到(正如预期的那样)输出顺序是随机的,因为它是 运行 并行的。
您的 logMessage
函数在 func1
函数中不可见。即使 logMessage
函数在 func1
上面声明也是有效的。
对于这种简单的情况,您可以按如下方式使用 nested functions:
workflow test {
function func1 {
function logMessage {
Write-Output "logMessage"
}
Write-Output "Func 1"
logMessage
}
func1
}
test
输出:
PS D:\PShell> D:\PShell\SO770877.ps1
Func 1
logMessage
第一次使用 PowerShell 5,我在调用一个将消息写入文件的函数时遇到了问题。以下是我正在做的简化版本。
workflow test {
function logMessage {
param([string] $Msg)
Write-Output $Msg
}
function RemoveMachineFromCollection{
param([string]$Collection, [string]$Machine)
# If there's an error
LogMessage "Error Removing Machine"
# If all is good
LogMessage "successfully remove machine"
}
$Collections = DatabaseQuery1
foreach -parallel($coll in $Collections) {
logMessage "operating on $coll collection"
$Machines = DatabaseQuery2
foreach($Mach in $Machines) {
logMessage "Removing $Mach from $coll"
RemoveMachineFromCollection -Collection $coll -Machine $Mach
}
}
}
test
这是它生成的错误:
The term 'logMessage' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. + CategoryInfo : ObjectNotFound: (logMessage:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException + PSComputerName : [localhost]
我试过在文件中四处移动 logMessage 函数,甚至尝试过全局范围。
在任何其他语言中,我都可以从任何其他函数调用 logMessage。因为这就是函数的目的。
重用代码块的"Workflow way"是什么?
我是否需要创建一些加载到工作流中的日志记录模块?
Powershell 要求在使用前定义函数 ('lexical scope')。在您的示例中,您在定义 logMessage
函数之前调用它。
您还将示例构建为 Powershell 工作流。工作流有一些普通脚本没有的限制;您需要了解这些差异。我确实this search to find some descriptions and discussions of the differences; the first "hit"提供了很好的资料。我(还)没有发现任何关于函数是否可以在工作流中定义的说法,但我会非常谨慎地首先在函数(或工作流)中定义函数。
您可以将函数和函数调用移动到工作流中的 InlineScript
(PowerShell ScriptBlock),如下所示。
workflow test {
InlineScript
{
function func1{
Write-Output "Func 1"
logMessage
}
function logMessage{
Write-Output "logMessage"
}
func1
}
}
将输出:
Func 1
logMessage
正如@JeffZeitlin 在他的回答中提到的,工作流不是 PowerShell,而且限制更多。 InlineScript 块允许解释普通的 PowerShell 代码,但是范围将绑定到 InlineScript 块。例如,如果您在脚本块中定义函数,然后尝试调用 InlineScript 块外的 func1
函数(但仍在工作流中),它将失败,因为它超出了范围。
如果您在工作流外部或工作流内部但不在 InlineScript 块中定义这两个函数,也会发生同样的情况。
现在举例说明如何将此应用到 运行 一个 foreach -parallel
循环。
workflow test {
## workflow parameter
param($MyList)
## parallel foreach loop on workflow parameter
foreach -parallel ($Item in $MyList)
{
## inlinescript
inlinescript
{
## function func1 declaration
function func1{
param($MyItem)
Write-Output ('Func 1, MyItem {0}' -f $MyItem)
logMessage $MyItem
}
## function logMessage declaration
function logMessage{
param($MyItem)
Write-Output ('logMessage, MyItem: {0}' -f $MyItem)
}
## func1 call with $Using:Item statement
## $Using: prefix allows us to call items that are in the workflow scope but not in the inlinescript scope.
func1 $Using:Item
}
}
}
对此工作流的调用示例如下所示
PS> $MyList = 1,2,3
PS> test $MyList
Func 1, MyItem 3
Func 1, MyItem 1
Func 1, MyItem 2
logMessage, MyItem: 3
logMessage, MyItem: 2
logMessage, MyItem: 1
您会注意到(正如预期的那样)输出顺序是随机的,因为它是 运行 并行的。
您的 logMessage
函数在 func1
函数中不可见。即使 logMessage
函数在 func1
上面声明也是有效的。
对于这种简单的情况,您可以按如下方式使用 nested functions:
workflow test {
function func1 {
function logMessage {
Write-Output "logMessage"
}
Write-Output "Func 1"
logMessage
}
func1
}
test
输出:
PS D:\PShell> D:\PShell\SO770877.ps1
Func 1
logMessage