如何在自定义cmdlet中正确使用-verbose和-debug参数

时间:2010-11-29 07:06:15

标签: powershell parameters cmdlets cmdlet

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

假设我有一个这样的cmdlet:

function DoStuff() {
   [CmdletBinding()]

   PROCESS {
      new-item Test -type Directory 
   }
}

如果将-debug-verbose传递给我的函数,我想将该标记传递到new-item cmdlet。这样做的正确模式是什么?

8 个答案:

答案 0 :(得分:33)

$PSBoundParameters不是你想要的。除了提供详细标记外,使用[CmdletBinding()]属性还允许在脚本中使用$PSCmdlet。事实上,你应该使用同样的详细信息。

通过[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,以及根本不存在开关的情况。

答案 1 :(得分:31)

也许这听起来很奇怪但是cmdlet没有简单的方法来了解其详细或调试模式。看看相关的问题:

How does a cmdlet know when it really should call 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

无论如何,这一切都不完美。如果有更好的解决方案,那么我真的很想了解它们。

答案 2 :(得分:26)

没有必要。 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不同的事情,你可以阅读here如何自己检查。

答案 3 :(得分:5)

有恢复和旧线程的风险。这是我的解决方案。

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设置为$:

DoStuff -Verbose
DoStuff -Verbose:$true
DoStuff -Debug
DoStuff -Debug:$true

答案 4 :(得分:2)

您可以根据绑定的debug或verbose参数构建新的哈希表,然后将其splat到内部命令。如果你只是指定开关(并且没有传递假开关,比如$ debug:$ false),你可以检查是否存在debug或verbose:

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 
   }  
}  

答案 5 :(得分:2)

最好的方法是设置$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>>
   }

答案 6 :(得分:1)

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

脚本:

$global:VerbosePreference = $VerbosePreference
Your-CmdLet

您-小命令:

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

明确检查&#39;继续&#39;当您从没有设置全局变量的脚本调用CmdLet时,脚本等于-verbose:$false(在这种情况下,它是$null

答案 7 :(得分:0)

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

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