PowerShell 5中的写主机与写信息

won*_*sia 24 powershell powershell-5.0

众所周知,这Write-Host是邪恶的.In PowerShell 5,Write-Information被添加并被认为是替换Write-Host.

但是,真的,哪个更好?
Write-Host因为它不使用管道是邪恶的,所以输入消息不能被重用.
但是,Write-Host只是在控制台中展示一些东西呢?在什么情况下我们应该重用输入?
无论如何,如果我们真的想重用输入,为什么不写这样的东西:

$foo = "Some message to be reused like saving to a file"
Write-Host $foo
$foo | Out-File -Path "D:\foo.log"
Run Code Online (Sandbox Code Playgroud)

另一个缺点Write-Host是,Write-Host可以通过使用-ForegroundColor和指定消息在控制台中显示的颜色-BackgroundColor.

另一方面,通过使用Write-Information,输入消息可以通过No.6管道在任何我们想要的地方使用.并且不需要像我上面写的那样编写额外的代码.但是黑暗的一面是,如果我们想要将消息写入控制台并保存到文件中,我们必须这样做:

# Always set the $InformationPreference variable to "Continue"
$InformationPreference = "Continue";

# if we don't want something like this:
# ======= Example 1 =======
# File Foo.ps1
$InformationPreference = "Continue";
Write-Information "Some Message"
Write-Information "Another Message"

# File AlwaysRunThisBeforeEverything.ps1
.\Foo.ps1 6>"D:\foo.log"
# ======= End of Example 1 =======

# then we have to add '6>"D:\foo.log"' to every lines of Write-Information like this:
# ======= Example 2 =======
$InformationPreference = "Continue";
Write-Information "Some Message" 6>"D:\foo.log"
Write-Information "Another Message" 6>"D:\foo.log"
# ======= End of Example 2 =======
Run Code Online (Sandbox Code Playgroud)

我觉得有点多余.

我只知道这个"vs"事情的一个方面,我必须要有一些东西.那么还有什么能让我相信Write-Information的更好Write-Host,请在这里留下你的答案.
谢谢.

Ans*_*ers 34

Write-*cmdlet,您可以引导你的PowerShell代码输出结构化的方式,所以你可以很容易地从彼此区分不同严重程度的信息.

  • Write-Host:在控制台上向交互式用户显示消息.与其他Write-*cmdlet 不同,这个cmdlet既不适合也不打算用于自动化/重定向目的.不是邪恶,只是不同.
  • Write-Output:将代码的"正常"输出写入默认(成功)输出流("STDOUT").
  • Write-Error:将错误信息写入单独的流("STDERR").
  • Write-Warning:将您认为警告的消息(即非故障的内容,但用户应该关注的内容)写入单独的流.
  • Write-Verbose:将您认为比"正常"输出更详细的信息写入单独的流.
  • Write-Debug:将您认为与调试代码相关的信息写入单独的流.

Write-Information只是这种方法的延续.它可以让你实现你的输出(日志级别Debug,Verbose,Information,Warning,Error),并且仍然具有可用于定期输出成功输出流.

至于为什么Write-Host成为一个包装Write-Information:我不知道这个决定的实际原因,但我怀疑这是因为大多数人不明白Write-Host实际上是如何工作的,即它可以用于什么以及它应该是什么用于.


据我所知,没有一种普遍接受或推荐的方法来登录PowerShell.例如,你可以实现像@JeremyMontgomery在他的回答中建议的单一记录功能:

function Write-Log {
  Param(
    [Parameter(Mandatory=$true, Position=0)]
    [ValidateNotNullOrEmpty()]
    [string]$Message,
    [Parameter(Mandatory=$false, Position=1)]
    [ValidateSet('Error', 'Warning', 'Information', 'Verbose', 'Debug')]
    [string]$LogLevel = 'Information'
  )

  switch ($LogLevel) {
    'Error'       { ... }
    'Warning'     { ... }
    'Information' { ... }
    'Verbose'     { ... }
    'Debug'       { ... }
    default       { throw "Invalid log level: $_" }
  }
}

Write-Log 'foo'                    # default log level: Information
Write-Log 'foo' 'Information'      # explicit log level: Information
Write-Log 'bar' 'Debug'
Run Code Online (Sandbox Code Playgroud)

或一组日志记录功能(每个日志级别一个):

function Write-LogInformation {
  Param(
    [Parameter(Mandatory=$true, Position=0)]
    [ValidateNotNullOrEmpty()]
    [string]$Message
  )

  ...
}

function Write-LogDebug {
  Param(
    [Parameter(Mandatory=$true, Position=0)]
    [ValidateNotNullOrEmpty()]
    [string]$Message
  )

  ...
}

...

Write-LogInformation 'foo'
Write-LogDebug 'bar'
Run Code Online (Sandbox Code Playgroud)

另一种选择是创建自定义记录器对象:

$logger = New-Object -Type PSObject -Property @{
  Filename = ''
  Console  = $true
}
$logger | Add-Member -Type ScriptMethod -Name Log -Value {
  Param(
    [Parameter(Mandatory=$true, Position=0)]
    [ValidateNotNullOrEmpty()]
    [string]$Message,
    [Parameter(Mandatory=$false, Position=1)]
    [ValidateSet('Error', 'Warning', 'Information', 'Verbose', 'Debug')]
    [string]$LogLevel = 'Information'
  )

  switch ($LogLevel) {
    'Error'       { ... }
    'Warning'     { ... }
    'Information' { ... }
    'Verbose'     { ... }
    'Debug'       { ... }
    default       { throw "Invalid log level: $_" }
  }
}
$logger | Add-Member -Type ScriptMethod -Name LogDebug -Value {
  Param([Parameter(Mandatory=$true)][string]$Message)
  $this.Log($Message, 'Debug')
}
$logger | Add-Member -Type ScriptMethod -Name LogInfo -Value {
  Param([Parameter(Mandatory=$true)][string]$Message)
  $this.Log($Message, 'Information')
}
...

Write-Log 'foo'                    # default log level: Information
$logger.Log('foo')                 # default log level: Information
$logger.Log('foo', 'Information')  # explicit log level: Information
$logger.LogInfo('foo')             # (convenience) wrapper method
$logger.LogDebug('bar')
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,您都可以通过外部化日志代码


mkl*_*nt0 16

为了补充Ansgar的有用和全面的答案:

Write-Host变成(实质上)
Write-Information -InformationAction Continue
PSv5中的包装器,大概是因为:

  • 可以抑制或重定向Write-Host消息,这在以前是不可能的(在PowerShell 4或更低版本中,Write-Host绕过PowerShell的流并直接输出到主机),

  • 同时保留向后兼容性,默认情况下输出消息- Write-Information与其默认行为是静默的(因为它尊重偏好变量$InformationPreference,其默认值为SilentlyContinue).

虽然Write-Host现在(PSv5 +)有点用词不当 - 它不一定写入主机 - 它仍然有一个明显的优势Write-Information(如你所述):它可以-ForegroundColor产生彩色输出-BackgroundColor.


Ansgar的答案涵盖了传统的日志记录视角,但PowerShell的Start-Transcriptcmdlet 可以作为内置替代方案(见下文).

至于您希望将消息输出到主机,同时还要在日志文件中捕获它们:

PowerShell的会话记录 - 通过Start-TranscriptStop-Transcript - 可能会给你你想要的.

顾名思义,成绩单捕获任何打印到屏幕上(没有着色),因此默认情况下包括成功输出.
适用于您的示例:

$null = Start-Transcript "D:\foo.log"

$InformationPreference = "Continue"
Write-Information "Some Message"
Write-Information "Another Message"

$null = Stop-Transcript
Run Code Online (Sandbox Code Playgroud)

以上将打印消息,在屏幕成绩单文件; 请注意,奇怪的是,只有在文件中它们才会带有前缀INFO:.
(与此相反,Write-Warning,Write-VerboseWrite-Debug-如果配置成产生输出-使用前缀WARNING:,VERBOSE:,DEBUG:都在屏幕上,在该文件中;类似地,Write-Error既产生在屏幕上,在该文件中的"噪声"多行输入).

注意一个奇怪的事情(我不清楚它是一个bug还是设计,在PSv5.1中观察到):输出来自Write-Information转录文件(但不在屏幕上),即使$InformationPreference设置为SilentlyContinue(默认值); 排除Write-Information输出的唯一方法(通过首选项变量或-InformationAction参数)似乎是一个值Ignore- 它会明确地使输出静音- 或者奇怪的Continue是,它只会打印到控制台,就像PetSerAl指出的那样.

概括地说,你可以使用Start-Transcript作为一种方便,内置近似一个日志工具,其详细程度可以从通过偏好变量(外部控制$InformationPreference,$VerbosePreference...) ,具有以下重要区别常规测井:

  • 通常,转录文件中的内容也会输出到控制台(通常可以认为是加号).

  • 但是,成功输出(数据输出)默认也会发送到成绩单 - 除非您捕获它或完全禁止它 - 并且您无法选择性地将其保留在成绩单之外:

    • 如果您捕获或禁止它,它将不会显示在主机(控制台,默认情况下)[1].

    • 但是,反过来是可能的:您可以通过Thanks,PetSerAl将输出发送到转录本(不在控制台中回显它); 例如,Out-Default -Transcript
      'to transcript only' | Out-Default -Transcript

  • 通常, Windows PowerShell v5.1/PowerShell Core v6.0.0-beta.5开始,外部重定向会将流保留在转录本之外,但有两个例外:

    • Write-Host输出,即使使用6>*>重定向.
    • 错误输出,即使使用2>*>重定向也是如此.
      但是,使用$ErrorActionPreference = 'SilentlyContinue'/ 'Ignore' 确实会将非终止错误保留在转录本之外,但不会终止转义.
  • 脚本文件不是面向行的(有一个带有调用信息的标题行块,并且无法保证脚本生成的输出仅限于一行),因此您不能指望逐行解析它们方式.


[1] PetSerAl提到了以下有限且有些麻烦的解决方法(PSv5 +),仅用于将成功输出发送到控制台,这显着排除了通过管道发送输出或捕获它:
'to console only' | Out-String -Stream | ForEach-Object { $Host.UI.WriteLine($_) }

  • @PatrickFranchise:嗯,这是 PSv5.1 的 _actual_ 行为 - 不确定它是否是 _expected_;坦率地说,我怀疑是一个错误。 (2认同)

Mic*_*ter 5

PowerShell 是关于自动化的。

有时,您一天多次运行脚本,并且不想一直看到输出。

Write-Host没有隐藏输出的可能性。无论如何,它都会写在控制台上。

使用Write-Information,您可以-InformationAction在脚本上指定参数。使用此参数,您可以指定是否要查看消息 ( -InformationAction Continue) 或不查看( -InformationAction SilentlyContinue)

编辑:也请使用"Some Message" | out-file D:\foo.log日志记录,也不Write-HostWrite-Information