首页 > 解决方案 > 使用 SilentlyContinue 时如何处理 Write-Information 管道输出

问题描述

Write-Information$InformationPreference当is时,似乎会在管道中输出不需要的记录SilentlyContinue。特别是因为默认值是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

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

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

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

InformationRecords 的管道是否损坏,或者作为管道使用者,我如何正确过滤掉不需要的 InformationRecords?

标签: powershellstreampipeline

解决方案


耐人寻味。但是,有一个基于About CommonParameters帮助主题中的以下语句的解决方法:

-InformationAction:Ignore抑制信息性消息并继续运行该命令。不像SilentlyContinueIgnore完全忘记信息;它不会将信息消息添加到信息流中。

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

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

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

当参数在命令中用于运行脚本或函数时,该InformationAction参数将覆盖但不替换首选项变量的值。$InformationAction

脚本

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\SO\57303102a.ps1
VERBOSE: verbose…………SilentlyContinue
SilentlyContinue:InfoAction=SilentlyContinue
VERBOSE: verbose…………Continue
        Continue:InfoAction=Continue
VERBOSE: verbose…………Ignore
          Ignore:
VERBOSE: verbose…………Ignore
         Default:
redirected: verbose…………Ignore

编辑。

不幸的是,如果我的函数正在调用其他人使用的代码,Write-Information 那么当我的代码重定向(或捕获)信息流时,它并没有真正解决核心问题,那么我无法丢弃用 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\SO\57303102c.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

推荐阅读