Powershell:来自函数的意外 return 值,使用 $args 访问参数

Powershell: unexpected return value from function, use of $args to access parameters

好的,我已经用不同的语言编写了很长一段时间,但我没有得到函数的 Powershells 概念 return?....

我是 Powershell 的新手,所以我确信我遗漏了一些非常基本的东西。

我有以下功能:

function plGetKeyValue ([string] $FileName, [string] $SectionName, [string] $Key) 
{
    if ($PSBoundParameters.Count -lt 2 -or $PSBoundParameters.Count -gt 3  )
    {
        "Invalid call to {0} in {1}" -f $MyInvocation.MyCommand.Name, 
                                        $MyInvocation.MyCommand.ModuleName
        return 
    }

    # Declaration
    $lFileContents  = ""
    $lSections      = ""
    $lDataStart     = ""
    $lStart         = -1
    $lEnd           = -1
    $lFoundSection  = ""
    $lNextSection   = ""
    $lResults       = ""
    $lRetValue      = ""

    # Handle the optional parameter.
    if ( $PSBoundParameters.Count -eq 2  ) {
        $PSBoundParameters.Add('Key', $SectionName)
        $PSBoundParameters.Remove('SectionName')        
        $Key = $SectionName
        $SectionName = $null
    }


    # Read the file in 
    $lFileContents  = Get-Content $FileName | Select-String -Pattern .* 

    # Get the sections.
    $lSections = $lFileContents -match '\[' 
    $lSections = $lSections -notmatch '#' 

    # Start of the data.
    $lDataStart = $lFileContents | Select-String -Pattern "^#", "^$" -NotMatch `
                                 | select-object -First 1

    # We have a section.
    if ( $PSBoundParameters.ContainsKey( 'SectionName' ) ) {

        # Find the section.
        $lFoundSection = $lSections | Select-String -Pattern "$lSectionName\b"

        # If none found we are out.
        if ( -Not $lFoundSection) { return $lRetValue }

        # Starting point for the key search is the line following 
        # the found section.
        $lStart = $lFoundSection[0].LineNumber

        # Loop through the sections and find the one after the found one.
        $lNextSection = $lSections | ForEach-Object { 
            # If we hit it, break.
            if ($_.LineNumber -gt $lStart) {
                break;
            }
        } 

        # Set the ending line for the search to the end of the section
        # or end of file.  Which ever we have.
        if ($lNextSection) {
            $lEnd = $lNextSection[0].LineNumber
        } else {
            $lEnd = $lFileContents[-1]
        }
    } else {
    # No section.
        $lStart = $lDataStart.LineNumber

        # Set the ending line for the search to the end of the section
        # or end of file.  Which ever we have.
        if ($lSections) {
            $lEnd = $lSections[0].LineNumber
        } else {
            $lEnd = $lFileContents[-1]
        }
    }

    # Extract the lines starting with the key.
    $lResults = $lFileContents[$lStart..$lEnd] -match "$Key\b"     
    # We got results.

    # Split the value off.
    return $lRetValue = $lResults[0] | Select -ExpandProperty "Line" 
}

创建此函数的过程引发了几个我研究过并感到困惑的问题

1) 文档指出应该使用 $args 来确定参数。它似乎永远不会为我填充?我正在使用版本 4?作为替代方案,我使用了 $PSBoundParameters。这是否可取?

2) 基于大量阅读和挠头,我发现 return 函数的值 rturn 所有未捕获的输出到管道。有人可以澄清未捕获的吗?

例如,我希望下面的函数return 变量$lRetValue 中的一个字符串。目前,它是 returning True。基于此,我相信我有一些未捕获的东西?但是我正在执行的所有内容都被捕获在一个变量中。我错过了什么?

调用例程正在调用以下形式的代码:

$FileName = "S:\PS\Home\GlobalConfig\jobs.cfg"
$key = "Help"
$section = "Section"

$r = plGetKeyValue $FileName $Key
write-host "r is:  $r"

输出结果如下:

PS C:> S:\PS\Home\Job\Test.ps1 r 是:真

如有任何帮助,我们将不胜感激。

术语说明:有些武断,下面我会区分参数参数
- 参数作为定义为函数声明一部分的占位符
- 与 arguments 不同,因为 values 绑定到给定调用中的占位符。

概念信息:

1) The documentation indicates that $args should be used to determine arguments.

$args 是一种 后备 机制,用于在 非高级 [=151] 中检查 未绑定 参数=](非 cmdlet)函数。

$args 已填充:

  • 仅当您的函数不是一个高级函数(函数被标记为高级函数存在 param(...) 参数声明语句 - 与在 function someFunc(...) 中声明参数相反) - 如果装饰有 [CmdletBinding()] 属性 ).

  • 即便如此,它也仅包含 未绑定 参数(未映射到声明参数的参数)。

换句话说:只有当你声明你的函数时根本没有任何参数时,$args 才包含 所有 个传递的参数。

相反,在高级函数中不能未绑定参数,并使用无法绑定的参数调用高级函数参数简单地失败(产生错误)。

由于定义 高级 函数通常是可取的,因为它们最好与 PowerShell 基础结构作为一个整体集成,所以最好完全不使用 $args
相反,使用多个 parameter sets and/or 数组参数的组合来覆盖所有可能的有效输入参数场景。

$PSBoundArguments 包含绑定到 声明的 参数的参数,并且通常 不需要 ,因为对应于可以直接使用参数名称(例如 $SectionName)。 (它有特殊用途,例如通过 splat @PSBoundArguments 将所有绑定参数传递给另一个 cmdlet/function)。

2) Based on a lot of reading and head scratching, I have found that return values from functions return all uncaptured output to the pipeline. Can someone, please clarify "uncaptured"?

通常,任何生成输出的 PowerShell 语句或表达式都会发送到 成功流(大致相当于 Unix shell 中的 stdout default,除非输出是 captured(例如,通过分配给变量)或 redirected(例如,通过将输出发送到文件)。

因此,与大多数编程语言的行为相反,如果您不希望语句产生输出,则必须采取行动。

如果您对语句的输出不感兴趣(而不是捕获/重定向它供以后使用),您可以重定向到 $null(等效/dev/null),管道到 cmdlet Out-Null,或分配给虚拟变量 $null ($null = ...).

因此,从某种意义上说,您可以调用 发送到成功流的输出 uncaptured.

然而,这与 return 声明无关

return 语句 的工作方式与其他语言相同;它在 PowerShell 中的主要目的是作为 control-flow 机制 - 退出函数或脚本块 - 而不是 output 数据(甚至虽然它可以用于此:带有一个参数,是另一种将输出发送到成功流的方法。


正在诊断您的具体问题:

有很多方法可以使您的函数成为更好的 PowerShell 公民[1] ,但您的直接问题是:

 $PSBoundParameters.Remove('SectionName')

returns 布尔值 发送到输出流,因为您既不抑制、捕获也不重定向它.在你的例子中,由于 $SectionName 参数 绑定的,它确实在 $PSBoundParameters 中有一个条目,所以 $PSBoundParameters.Remove('SectionName') returns $true.

要抑制这种不需要的输出,请使用如下内容:

 $null = $PSBoundParameters.Remove('SectionName')

一般来说,除非你知道一条语句不会产生输出,否则最好是安全地在前面添加$null =(或使用等效的机制来抑制输出).

尤其是直接 方法 调用对象时,通常不清楚是否会返回一个值 - 变成输出(发送到成功流)。


[1] 以下帮助主题提供了更多信息:
- 参数的使用,包括如何使用 help -Full / -Detailed ...:
检查它们 help about_Parameters
- 定义简单函数及其参数:
help about_functions,
从中您可以进步到 高级 功能:
help about_functions_advanced
及其参数定义:
help about_Functions_Advanced_Parameters