【问题标题】:How does a cmdlet know when it really should call WriteVerbose()?cmdlet 如何知道它何时真正应该调用 WriteVerbose()?
【发布时间】:2011-02-16 01:12:09
【问题描述】:

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

也许我错过了一些简单的事情,但我找不到答案。所有 cmdlet 到目前为止我看到的实现只是调用WriteVerbose() 没有任何 犹豫。我知道这样做是正确,但它不是有效

详细模式关闭但 cmdlet 仍在准备时,性能会受到影响 WriteVerbose() 调用的数据,也就是说,一无所有。

换句话说,在 cmdlet 中我希望能够:

if (<VerboseMode>)
{
    .... data preparation, sometimes expensive ...
    WriteVerbose(...);
}

但我不知道如何获得这个if (&lt;VerboseMode&gt;)。有什么想法吗?


结论: @stej 的答案显示了如何在理论上获得所需的信息。在实践中,这是 hacky 并且不太适合。因此,如果 cmdlet 产生非常昂贵的详细或调试输出,那么引入一个指定详细级别的附加参数看起来是合理的。

【问题讨论】:

  • 我不同意你的结论。请参阅我的答案以获得解决方案。

标签: powershell


【解决方案1】:

这是来自System.Management.Automation.MshCommandRuntime的方法。

internal void WriteVerbose(VerboseRecord record)
{
    if ((this.Host == null) || (this.Host.UI == null))
    {
        tracer.TraceError("No host in CommandBase.WriteVerbose()", new object[0]);
        throw tracer.NewInvalidOperationException();
    }
    ActionPreference verbosePreference = this.VerbosePreference;
    if (this.WriteHelper_ShouldWrite(verbosePreference, this.lastVerboseContinueStatus))
    {
        if (record.InvocationInfo == null)
        {
            record.SetInvocationInfo(this.MyInvocation);
        }
        this.CBhost.InternalUI.WriteVerboseRecord(record);
    }
    this.lastVerboseContinueStatus = this.WriteHelper(null, null, verbosePreference, this.lastVerboseContinueStatus, "VerbosePreference");
}

MshCommandRuntime 实现了接口ICommandRuntime,它对详细程度一无所知:| (通过反射器发现)。 MshCommandRuntime 的实例应该在 Cmdlet (public ICommandRuntime CommandRuntime { get; set; }) 中可用。

所以应该可以将属性CommandRuntime 转换为MshCommandRuntime 并检查详细程度。不管怎样,这真的很丑。


我完全同意应该有一种简单的方法来找出它。除此之外(梦想中的)编译器应该足够聪明,不会在这种情况下评估某些字符串:

$DebugPreference = 'SilentlyContinue'
$array = 1..1000
Write-Debug "my array is $array"

永远不会使用Write-Debug 的输入,因此不应在传递的字符串中评估$array。(可以测试它是否真的被评估为:Write-Debug "my array is $($array|%{write-host $_; $_})"

【讨论】:

  • @stej:感谢您的有用调查。从理论上讲,应该可以使用反射以这种方式破解(因为大多数东西都是内部的,无法正常访问)。但这当然不是一个实际的解决方案。不过,如果我们找不到更好的选择,我会在一段时间内接受答案。我还在 Connect 上找到了一个相关建议:connect.microsoft.com/PowerShell/feedback/details/74811/…(我认为这还不够;我们需要一个标志 IsVerbose、IsDebug 等)
  • @Roman,已投票。 |目前我想到的是 - 在开发 cmdlet 时,您应该可以访问所有变量,不是吗。然后就有可能得到$DebugPrecedence 并采取相应的行动。不理想,但应该可以。
  • $DebugPreference, $VerbosePreference 确实聊胜于无。但它们还不够,因为它们被 cmdlet 无处不在的参数 -Verbose-Debug(如果指定)覆盖。但是这些参数不能从 cmdlet 访问。参数是否会隐式更改本地$DebugPreference$VerbosePreference?我对此表示怀疑,但我会尝试找出答案。
  • “无处不在的参数会改变本地的$DebugPreference,$VerbosePreference吗?” – 不,他们没有。好吧,基本上我得出的结论是,在昂贵的详细或调试输出的情况下,cmdlet 应该引入额外的开关甚至参数,类似于-VerboseLevel &lt;int | string | enum&gt;。嗯,也许这不是一个坏主意,实际上。
  • 是的,在某些脚本中,我就是这样使用的。更多的详细程度来获得我真正想要的东西。除此之外......我摆脱了“VERBOSE:”和“DEBUG:”前缀。
【解决方案2】:

怎么样:

BEGIN {
    if ($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent) {
        $HasDebugFlag = $true
    }

    if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent) {
        $HasVerboseFlag = $true
    }
}
PROCESS {
    if ($HasVerboseFlag) {
        expensive_processing
    }
}

警告:仅在 PowerShell 3 上测试。

【讨论】:

  • 确实不错。 +1。这应该在某些情况下有效,实际上,很多。如果模式不是通过参数设置而是通过$VerbosePreference$DebugPreference 设置的,它将不起作用。因此,if-s 也应该检查这些变量。但是 V3 引入了自定义默认值。如果通过自定义默认值启用模式,绑定参数可能无济于事。
  • 如果从另一个 cmdlet 调用一个 cmdlet 则不起作用。
【解决方案3】:

因此,我们不仅要考虑 cmdlet 的公共参数 -Debug 和 -Verbose,还要考虑全局 $DebugPreference 和 $VerbosePreference 标志,以及如果 cmdlet 从另一个 cmdlet。

无需修改内部结构即可完成。

This answer 向您展示了它可以在 PowerShell cmdlet 中解决一些小问题。

function f { [cmdletbinding()]Param() 
    $debug = $DebugPreference -ne 'SilentlyContinue'
    $verbose = $VerbosePreference -ne 'SilentlyContinue'
    "f is called"
    "    `$debug = $debug"
    "    `$verbose = $verbose"
}
function g { [cmdletbinding()]Param() 
    "g is called"
    f 
}
f
f -Debug -Verbose
g
g -Debug -Verbose

在 C# 中,我们必须检查这些全局标志以及公共参数。请务必从 PSCmdlet 而不是 Cmdlet 继承以获取 GetVariableValue 方法。

bool debug = false;
bool containsDebug = MyInvocation.BoundParameters.ContainsKey("Debug");
if (containsDebug)
    debug = ((SwitchParameter)MyInvocation.BoundParameters["Debug"]).ToBool();
else
    debug = (ActionPreference)GetVariableValue("DebugPreference") != ActionPreference.SilentlyContinue;

bool verbose = false;
bool containsVerbose = MyInvocation.BoundParameters.ContainsKey("Verbose");
if (containsVerbose)
    verbose = ((SwitchParameter)MyInvocation.BoundParameters["Verbose"]).ToBool();
else
    verbose = (ActionPreference)GetVariableValue("VerbosePreference") != ActionPreference.SilentlyContinue; 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多