Powershell:函数的意外返回值,使用$args访问参数

Powershell:函数的意外返回值,使用$args访问参数,powershell,parameters,return-value,Powershell,Parameters,Return Value,好的,我已经用不同的语言编写了很长一段时间了,但是我没有得到PowerShell函数返回的概念 我对Powershell非常陌生,所以我确信我遗漏了一些非常基本的东西 我有以下功能: function plGetKeyValue ([string] $FileName, [string] $SectionName, [string] $Key) { if ($PSBoundParameters.Count -lt 2 -or $PSBoundParameters.Count -gt 3

好的,我已经用不同的语言编写了很长一段时间了,但是我没有得到PowerShell函数返回的概念

我对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) 基于大量的阅读和理解,我发现函数rturn的返回值都是未捕获的输出到管道。有人能澄清一下吗

例如,我希望下面的函数在变量$lRetValue中返回一个字符串。目前,它正在返回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:是的

非常感谢您的帮助。

术语说明:我将在下面区分参数和参数:
-作为函数声明一部分定义的占位符的参数,
-与参数不同,就像在给定调用中绑定到占位符的值一样

概念信息:

1) 文档指出,应该使用$args来确定参数

$args
是一种回退机制,用于检查非高级(非cmdlet)函数中的未绑定参数

$args
已填充:

  • 仅当您的函数不是高级函数时(函数通过存在
    param(…)
    参数声明语句标记为高级函数,而不是在
    function someFunc(…)
    中声明参数,如果使用
    [CmdletBinding()]
    属性修饰)

  • 即使如此,它也只包含未绑定的参数(未映射到已声明参数的参数)

换句话说:
$args
仅当您声明的函数没有任何参数时才会包含所有传递的参数

相反,在高级函数中不能有未绑定的参数,使用无法绑定到参数的参数调用高级函数将失败(生成错误)

由于定义高级功能通常是可取的,因为它们最好与PowerShell基础架构作为一个整体集成,因此最好不使用
$args

相反,使用多个和/或数组参数的组合来覆盖所有可能的有效输入参数场景

$PSBoundArguments
包含绑定到已声明参数的参数,通常不需要,因为与参数名称相对应的变量名称(例如,
$SectionName
)可以直接使用。(它有专门用途,例如通过splat
@PSBoundArguments
将所有绑定参数传递给另一个cmdlet/函数)

2) 通过大量阅读和讨论,我发现函数的返回值将所有未捕获的输出返回到管道。有人能澄清一下“不适应”吗

通常,默认情况下,生成输出的任何PowerShell语句或表达式都会发送到成功流(与Unix shell中的stdout大致类似),除非捕获了输出(例如,通过分配给变量)或重定向了输出(例如,通过将输出发送到文件)

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

如果您对语句的输出不感兴趣(而不是捕获/重定向它以供以后使用),则可以重定向到
$null
(相当于
/dev/null
)、管道到cmdlet
输出null
,或分配给伪变量
$null
$null=…

因此,可以说,您可以调用发送到未捕获成功流的输出

但是,这与
return
语句无关:

return
语句的工作方式与其他语言不同;它在PowerShell中的主要用途是作为一种控制流机制(退出函数或脚本块),而不是作为一种输出数据的机制(尽管它也可以用于此目的:使用参数是将输出发送到成功流的另一种方式)


诊断您的特定问题:

有许多方法可以使您的函数成为更好的PowerShell公民[1] ,但你眼前的问题是:

 $PSBoundParameters.Remove('SectionName')
返回发送到输出流的布尔值,因为您既不抑制、捕获也不重定向它。在您的情况下,由于
$SectionName
参数已绑定,因此它在
$PSBoundParameters
中确实有一个条目,因此
$PSBoundParameters.Remove('SectionName')
返回
$true

要抑制此不需要的输出,请使用以下方法:

 $null = $PSBoundParameters.Remove('SectionName')
一般来说,除非您知道某个语句不生成输出,否则最好是安全的,并在
$null=
前面加上前缀(或者使用等效的机制来抑制输出)

特别是对于对象的直接方法调用,通常不清楚是否会返回一个值,该值将转换为输出(发送到成功流)


[1] 以下帮助主题提供了更多信息:
-参数的使用,包括如何使用
帮助检查参数-