使用 SilentlyContinue 时如何处理 Write-Information 管道输出

How to process Write-Information pipeline output when using SilentlyContinue

$InformationPreferenceSilentlyContinue 时,

Write-Information 似乎会在管道中输出不需要的记录。特别是因为默认值是 SilentlyContinue,我认为这是个问题。

function foo {
    $VerbosePreference = 'SilentlyContinue'
    Write-Verbose "verbose silent"

    $VerbosePreference = 'Continue'
    Write-Verbose "verbose continue"

    $InformationPreference = 'SilentlyContinue'
    Write-Information "info silent"

    $InformationPreference = 'Continue'
    Write-Information "info continue"
}

$PSVersionTable.PSVersion.ToString()
# 5.1.15063.1805

foo
# VERBOSE: verbose continue
# info continue

foo *>&1 | % { "redirected: $_" }
# redirected: verbose continue
# redirected: info silent
# redirected: info continue

正如预期的那样,SilentlyContinue$VerbosePreference 导致 Write-Verbose 不生成消息,因此没有要重定向的消息。

同样如预期的那样,SilentlyContinue$InformationPreference 在控制台上正确显示任何内容,但是一条消息被重定向到管道。

这似乎是错误的,因为一旦消息进入管道,消费者如何确定是否应静默丢弃该信息消息?

InformationRecords 的流水线是否损坏,或者作为流水线消费者如何正确过滤掉不需要的 InformationRecords?

有趣。但是,有一种解决方法基于 About CommonParameters 帮助主题中的以下语句:

-InformationAction:Ignore suppresses the informational message and continues running the command. Unlike SilentlyContinue, Ignore completely forgets the informational message; it doesn't add the informational message to the information stream.

在下面的脚本中,我做了一些改动:

  1. 将函数输出设置为变量(当然,您也可以改为传入管道;请参阅最后的脚本行 Debug-InfoAction *>&1 | ForEach-Object {"redirected: $_"} 及其结果)。

  2. InformationAction参数(不幸的是,Ignore不支持ActionPreference变量;因此,这个调整成为解决方案的关键点)。

The InformationAction parameter overrides, but does not replace the value of the $InformationAction preference variable when the parameter is used in a command to run a script or function.

脚本

function Debug-InfoAction {
    param(
        [System.Management.Automation.ActionPreference]
        $InfoAction='Ignore'
    )
    Write-Verbose     "verbose…………$InfoAction" -Verbose
    Write-Information "InfoAction=$InfoAction" -InformationAction $InfoAction
}

foreach ( $InfoAction in @(
                'SilentlyContinue',
                'Continue', 
                'Ignore'
            ) # exclude from testing: 'Stop', 'Inquire', 'Suspend'
         ) {
    $aux = ''
    $aux = Debug-InfoAction -InfoAction $InfoAction 6>&1
    '{0,16}:{1}' -f $InfoAction, $aux
}
# check newly defined default InformationAction
    $InfoAction = 'Default' 
    $aux = ''
    $aux = Debug-InfoAction                         6>&1
    '{0,16}:{1}' -f $InfoAction, $aux
    Debug-InfoAction *>&1 | ForEach-Object { "redirected: $_" }

输出:

D:\PShell\SO303102a.ps1
VERBOSE: verbose…………SilentlyContinue
SilentlyContinue:InfoAction=SilentlyContinue
VERBOSE: verbose…………Continue
        Continue:InfoAction=Continue
VERBOSE: verbose…………Ignore
          Ignore:
VERBOSE: verbose…………Ignore
         Default:
redirected: verbose…………Ignore

编辑。

Unfortunately it doesn't really help the core problem that if my function is calling someone elses code that uses Write-Information then when my code redirects (or captures) the Information stream, then I have no way of discarding Information that was written with SilentlyContinue.

后面的合格谏示我的不足……下面简单截取一下:

$c = Write-Information "Continue" -InformationAction Continue         6>&1
$s = Write-Information "Silently" -InformationAction SilentlyContinue 6>&1

两者都是[System.Management.Automation.InformationRecord]类型。我试图找到差异(即使在它们的 子属性 中)但无法找到,除了数据本身中的差异:

param(
  [switch]$IncludeEqual = $false
)
begin {
  Function Compare-Property {
    param(
        $RefObject, 
        $DifObject,
        [switch]$IncludeEqual
    )
    # compare objects
    Compare-Object $RefObject $DifObject -IncludeEqual | 
      Select-Object -Property SideIndicator, 
                     @{N='objects';E={'Function Compare-Property'}},
                     InputObject
    Write-Verbose $RefObject.GetType().FullName -Verbose:$IncludeEqual

    # a level down: compare properties of objects individually

    $cpe = $RefObject.PsObject.Properties.GetEnumerator()
    $spe = $DifObject.PsObject.Properties.GetEnumerator()
    While ( $cpe.MoveNext() -and $spe.MoveNext() ) {
      Compare-Object $cpe.Current.Value `
                     $spe.Current.Value -IncludeEqual:$IncludeEqual |
        Select-Object -Property SideIndicator, 
                       @{N='objects';E={$spe.Current.Name}}, 
                       InputObject

        # more level down: 
        # compare properties of properties of objects individually

        $cpeIn = $cpe.Current.PsObject.Properties.GetEnumerator()
        $speIn = $spe.Current.PsObject.Properties.GetEnumerator()
        While ( $cpeIn.MoveNext() -and $speIn.MoveNext() ) {
          Compare-Object $cpeIn.Current.Value `
                         $speIn.Current.Value -IncludeEqual:$IncludeEqual |
            Select-Object -Property SideIndicator, 
                           @{N='objects';
                             E={ $cpe.Current.Name + '.' + $cpeIn.Current.Name}},
                           InputObject
        }      
    }
  }
  $c = Write-Information "Continue" -InformationAction Continue         6>&1
  $s = Write-Information "Silently" -InformationAction SilentlyContinue 6>&1
}
process {
  Compare-Object $c.GetType() $s.GetType() -IncludeEqual | 
    Select-Object -Property SideIndicator, 
                   @{N='objects';E={'.GetType()'}}, InputObject
  Compare-Object $c.PsObject.TypeNames `
                 $s.PsObject.TypeNames -IncludeEqual | 
    Select-Object -Property SideIndicator, 
                   @{N='objects';E={'.PsObject.TypeNames'}}, 
                   InputObject
  Compare-Object ($c | Get-Member -MemberType Properties -Force).PsTypeNames `
                 ($s | Get-Member -MemberType Properties -Force).PsTypeNames `
                  -IncludeEqual | 
    Select-Object -Property SideIndicator, 
                   @{N='objects';E={'(gm).PsTypeNames'}}, 
                   InputObject
  Compare-Object $c $s -IncludeEqual | 
    Select-Object -Property SideIndicator, 
                   @{N='objects';E={"$($c.GetType().Name) itself"}},
                   InputObject

  Compare-Property  -RefObject $c `
                    -DifObject $s          -IncludeEqual:$IncludeEqual

  Compare-Property  -RefObject $c.PsObject `
                    -DifObject $s.PsObject -IncludeEqual:$IncludeEqual
}

输出:

D:\PShell\SO303102c.ps1
SideIndicator objects                   InputObject
------------- -------                   -----------
==            .GetType()                System.Management.Automation.InformationRecord
==            .PsObject.TypeNames       System.Management.Automation.InformationRecord
==            .PsObject.TypeNames       System.Object
==            (gm).PsTypeNames          System.Object[]                        
==            (gm).PsTypeNames          System.Array                           
==            (gm).PsTypeNames          System.Object                          
=>            InformationRecord itself  Silently
<=            InformationRecord itself  Continue
=>            Function Compare-Property Silently
<=            Function Compare-Property Continue
=>            MessageData               Silently
<=            MessageData               Continue
=>            MessageData.Value         Silently
<=            MessageData.Value         Continue
==            Function Compare-Property psobject {Members, Properties, Methods, Imme...
=>            ImmediateBaseObject       Silently
<=            ImmediateBaseObject       Continue
=>            ImmediateBaseObject.Value Silently
<=            ImmediateBaseObject.Value Continue
=>            BaseObject                Silently
<=            BaseObject                Continue
=>            BaseObject.Value          Silently
<=            BaseObject.Value          Continue