50

默认情况下,任何具有 [CmdletBinding()] 属性的命名函数都接受-debugand -verbose(以及其他一些)参数,并具有预定义的$debug$verbose变量。我试图弄清楚如何将它们传递给在函数中调用的其他 cmdlet。

假设我有一个这样的 cmdlet:

function DoStuff() {
   [CmdletBinding()]

   PROCESS {
      new-item Test -type Directory
   }
}

如果-debug或被-verbose传递到我的函数中,我想将该标志传递到new-itemcmdlet。这样做的正确模式是什么?

4

8 回答 8

44

$PSBoundParameters不是你要找的。除了提供 Verbose 标志外,该[CmdletBinding()]属性的使用还允许在您的脚本中使用。$PSCmdlet实际上,您应该使用的是同一个 Verbose。

通过[CmdletBinding()],可以通过 访问绑定的参数$PSCmdlet.MyInvocation.BoundParameters。这是一个使用 CmdletBinding 的函数,只需立即输入嵌套提示符,即可检查函数范围内可用的变量。

PS D:\> function hi { [CmdletBinding()]param([string] $Salutation) $host.EnterNestedPrompt() }; hi -Salutation Yo -Verbose

PS D:\>>> $PSBoundParameters

____________________________________________________________________________________________________
PS D:\>>> $PSCmdlet.MyInvocation.BoundParameters

Key Value                                                                                                                                                                                                           
--- -----                                                                                                                                                                                                           
Salutation Yo                                                                                                                                                                                                              
Verbose   True                                                                                       

因此,在您的示例中,您需要以下内容:

function DoStuff `
{
    [CmdletBinding()]
    param ()
    process
    {
      new-item Test -type Directory `
        -Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true)
    }
}

这包括 -Verbose、-Verbose:$false、-Verbose:$true 以及开关根本不存在的情况。

于 2011-07-20T19:51:21.187 回答
38

也许这听起来很奇怪,但没有任何简单的方法让 cmdlet 知道它的详细或调试模式。看看相关的问题:

cmdlet 如何知道它何时真正应该调用 WriteVerbose()?

一个不完美但实际上合理的选项是引入您自己的 cmdlet 参数(例如,$MyVerbose$MyDebug)并在代码中明确使用它们:

function DoStuff {
    [CmdletBinding()]
    param
    (
        # Unfortunately, we cannot use Verbose name with CmdletBinding
        [switch]$MyVerbose
    )

    process {

        if ($MyVerbose) {
            # Do verbose stuff
        }

        # Pass $MyVerbose in the cmdlet explicitly
        New-Item Test -Type Directory -Verbose:$MyVerbose
    }
}

DoStuff -MyVerbose

更新

当我们只需要一个开关(而不是一个详细级别的值)时,使用的方法$PSBoundParameters可能比这个答案的第一部分中提出的更好(带有额外的参数):

function DoStuff {
    [CmdletBinding()]
    param()

    process {
        if ($PSBoundParameters['Verbose']) {
            # Do verbose stuff
        }

        New-Item Test -Type Directory -Verbose:($PSBoundParameters['Verbose'] -eq $true)
    }
}

DoStuff -Verbose

反正一切都不完美。如果有更好的解决方案,那么我真的很想亲自了解它们。

于 2010-11-29T08:49:48.667 回答
28

没有必要。正如下面的代码所证明的那样,PowerShell 已经做到了这一点。

function f { [cmdletbinding()]Param()    
    "f is called"
    Write-Debug Debug
    Write-Verbose Verbose
}
function g { [cmdletbinding()]Param() 
    "g is called"
    f 
}
g -Debug -Verbose

输出是

g is called
f is called
DEBUG: Debug
VERBOSE: Verbose

但是,它不像将 -Debug 传递给下一个 cmdlet 那样直接。它是通过 $DebugPreference 和 $VerbrosePreference 变量完成的。Write-Debug 和 Write-Verbose 的行为与您期望的一样,但如果您想使用 debug 或 verbose 做一些不同的事情,您可以在此处阅读如何自行检查。

于 2013-12-29T22:36:38.183 回答
10

这是我的解决方案:

function DoStuff {
    [CmdletBinding()]
    param ()

    BEGIN
    {
        $CMDOUT = @{
            Verbose = If ($PSBoundParameters.Verbose -eq $true) { $true } else { $false };
            Debug = If ($PSBoundParameters.Debug -eq $true) { $true } else { $false }
        }

    } # BEGIN ENDS

    PROCESS
    {
        New-Item Example -ItemType Directory @CMDOUT
    } # PROCESS ENDS

    END
    {

    } #END ENDS
}

这与其他示例的不同之处在于它将表示“-Verbose:$false”或“-Debug:$false”。如果您使用以下内容,它只会将 -Verbose/-Debug 设置为 $true:

DoStuff -Verbose
DoStuff -Verbose:$true
DoStuff -Debug
DoStuff -Debug:$true
于 2013-04-30T10:22:18.713 回答
3

您可以根据绑定的调试或详细参数构建一个新的哈希表,然后将其发送到内部命令。如果您只是指定开关(并且没有传递错误开关,例如 $debug:$false),您可以检查是否存在调试或详细信息:

function DoStuff() { 
   [CmdletBinding()] 

   PROCESS { 
        $HT=@{Verbose=$PSBoundParameters.ContainsKey'Verbose');Debug=$PSBoundParameters.ContainsKey('Debug')}
      new-item Test -type Directory @HT
   } 
} 

如果您想传递参数值,它会更复杂,但可以通过以下方式完成:

function DoStuff {  
   [CmdletBinding()]  
   param()
   PROCESS {  
   $v,$d = $null
   if(!$PSBoundParameters.TryGetValue('Verbose',[ref]$v)){$v=$false}
   if(!$PSBoundParameters.TryGetValue('Debug',[ref]$d)){$d=$false}
   $HT=@{Verbose=$v;Debug=$d} 
   new-item Test -type Directory @HT 
   }  
}  
于 2010-11-29T12:16:29.457 回答
3

最好的方法是设置$VerbosePreference. 这将为整个脚本启用详细级别。不要忘记在脚本结束时禁用它。

Function test
{
    [CmdletBinding()]
    param($param1)

    if ($psBoundParameters['verbose'])
    {
        $VerbosePreference = "Continue"
        Write-Verbose " Verbose mode is on"
    }
    else
    {
        $VerbosePreference = "SilentlyContinue"
        Write-Verbose " Verbose mode is Off"
    }


    # <Your code>

}
于 2014-05-13T19:31:40.363 回答
1

您可以在启动脚本时将 VerbosePreference 设置为全局变量,然后在自定义 cmdlet 中检查全局变量。

脚本:

$global:VerbosePreference = $VerbosePreference
Your-CmdLet

你的 CmdLet:

if ($global:VerbosePreference -eq 'Continue') {
   # verbose code
}

显式检查“继续”允许脚本等于-verbose:$false从未设置全局变量的脚本调用 CmdLet(在这种情况下为$null

于 2015-08-27T10:00:48.290 回答
-1

我认为这是最简单的方法:

Function Test {
    [CmdletBinding()]
    Param (
        [parameter(Mandatory=$False)]
        [String]$Message
    )

    Write-Host "This is INFO message"

    if ($PSBoundParameters.debug) {
        Write-Host -fore cyan "This is DEBUG message"
    }

    if ($PSBoundParameters.verbose) {
        Write-Host -fore green "This is VERBOSE message"
    }

    ""
}
Test -Verbose -Debug
于 2013-09-09T10:46:49.947 回答