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

Rom*_*min 6 powershell

如何做一个cmdlet的知道什么时候真的应该叫WriteVerbose(), WriteDebug()等?

也许我想念一些简单的东西,但我找不到答案.到目前为止我见过的所有cmdlet实现都WriteVerbose()没有任何犹豫.我知道这样做是正确的,但它没有效果.

当详细模式关闭但cmdlet仍然为WriteVerbose()调用准备数据时,性能会受到影响,即无需任何操作.

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

if (<VerboseMode>)
{
    .... data preparation, sometimes expensive ...
    WriteVerbose(...);
}
Run Code Online (Sandbox Code Playgroud)

但我不知道怎么做到这一点if (<VerboseMode>).有任何想法吗?


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

ste*_*tej 6

这是方法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");
}
Run Code Online (Sandbox Code Playgroud)

MshCommandRuntime实现ICommandRuntime对冗长度一无所知的接口:| (通过反射器找到).MshCommandRuntime应该在Cmdlet(public ICommandRuntime CommandRuntime { get; set; })中提供实例.

所以应该可以将属性转换CommandRuntimeMshCommandRuntime并检查详细程度.无论如何,这真的很难看.


我完全同意应该有一个简单的方法来找到它.除此之外(做梦)编译器应该足够聪明,不要在这样的情况下评估一些字符串:

$DebugPreference = 'SilentlyContinue'
$array = 1..1000
Write-Debug "my array is $array"
Run Code Online (Sandbox Code Playgroud)

输入Write-Debug将永远不会被使用,所以$array不应该在传递的字符串中进行评估..(可以测试它是否真的像这样评估:Write-Debug "my array is $($array|%{write-host $_; $_})"

  • @stej:感谢您的有用调查。从理论上讲,应该可以使用反射以这种方式进行黑客攻击(因为大多数东西都是内部的,不能正常访问)。但这当然不是一个实用的解决方案。尽管如此,如果我们没有找到更好的选择,我会在一段时间内接受答案。我还在 Connect 上找到了一个相关的建议:https://connect.microsoft.com/PowerShell/feedback/details/74811/performance-provide-formatting-overloads-for-writeverbose-writedebug-etc(但这还不够,我认为;我们需要一个标志 IsVerbose、IsDebug 等) (2认同)
  • `$DebugPreference`、`$VerbosePreference` 确实比没有好。但它们还不够,因为它们被 cmdlet 无处不在的参数“-Verbose”、“-Debug”(如果有的话)覆盖。但是无法从 cmdlet 访问这些参数。参数是否隐式地更改了本地的“$DebugPreference”、“$VerbosePreference”?我对此表示怀疑,但我会努力找出答案。 (2认同)

ser*_*ist 5

怎么样:

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

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

警告:仅在 PowerShell 3 上测试。

  • 这确实很好。+1。这应该在某些情况下有效,实际上,在很多情况下。如果模式不是通过参数设置而是通过`$VerbosePreference` 或`$DebugPreference` 设置,它将不起作用。因此,`if`-s 也应该检查这些变量。但是 V3 引入了自定义默认值。如果通过自定义默认值启用这些模式,则绑定参数可能无济于事。 (3认同)

Lar*_*ens 5

因此,我们不仅需要考虑cmdlet的公共参数-Debug和-Verbose,还要考虑全局$ DebugPreference和$ VerbosePreference标志,以及如果从另一个cmdlet调用cmdlet,则继承这些公共参数的事实.

它可以在内部没有黑客攻击的情况下完成.

这个答案向您展示了在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
Run Code Online (Sandbox Code Playgroud)

从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; 
Run Code Online (Sandbox Code Playgroud)