当 运行 在 powershell 中执行命令时,如何为 stdout/stderr 上的所有输出添加 date/time?
When running a command in powershell how can I prepend a date/time for all output on stdout/stderr?
当运行脚本为所有日志输出添加日期前缀时,是否可以在 powershell 中使用?
我知道可以这样做:
Write-Host "$(Get-Date -format 'u') 我的日志输出"
但我不想每次输出一行时都调用一些函数。相反,我想在 运行 任何脚本或命令时修改所有输出,并为每一行添加时间前缀。
objects generated by Write-Host
already come with a timestamp, you can use Update-TypeData
to override the .ToString()
Method from the InformationRecord
Class and then redirect the output from the Information Stream to the Success Stream.
Update-TypeData -TypeName System.Management.Automation.InformationRecord -Value {
return $this.TimeGenerated.ToString('u') + $this.MessageData.Message.PadLeft(10)
} -MemberType ScriptMethod -MemberName ToString -Force
'Hello', 'World', 123 | Write-Host 6>&1
在all输出前插入一个日期,即stdout,stderr和 PowerShell-specific streams,您可以使用重定向运算符 *>&1
重定向(合并)命令或脚本块的所有流,管道到 Out-String -Stream
以格式化流 objects 到 text 行,然后使用 ForEach-Object
处理每一行并在前面加上日期。
让我从一个简单的例子开始,可以在下面找到更完整的解决方案。
# Run a scriptblock
&{
# Test output to all possible streams, using various formatting methods.
# Added a few delays to test if the final output is still streaming.
"Write $($PSStyle.Foreground.BrightGreen)colored`ntext$($PSStyle.Reset) to stdout"
Start-Sleep -Millis 250
[PSCustomObject]@{ Answer = 42; Question = 'What?' } | Format-Table
Start-Sleep -Millis 250
Get-Content -Path not-exists -EA Continue # produce a non-terminating error
Start-Sleep -Millis 250
Write-Host 'Write to information stream'
Start-Sleep -Millis 250
Write-Warning 'Write to warning stream'
Start-Sleep -Millis 250
Write-Verbose 'Write to verbose stream' -Verbose
Start-Sleep -Millis 250
$DebugPreference = 'Continue' # To avoid prompt, needed for Windows Powershell
Write-Debug 'Write to debug stream'
} *>&1 | Out-String -Stream | ForEach-Object {
# Add date in front of each output line
$date = Get-Date -Format "yy\/MM\/dd H:mm:ss"
foreach( $line in $_ -split '\r?\n' ) {
"$($PSStyle.Reset)[$date] $line"
}
}
在 PS 7.2 控制台中输出:
- 使用
Out-String
我们使用标准的 PowerShell 格式化系统使输出看起来正常,因为它会在没有重定向的情况下出现(例如,表格之类的东西保持不变)。 -Stream
参数对于保持 PowerShell 的 streaming 输出行为至关重要。如果没有此参数,只有在整个脚本块完成后才会收到输出。
- 虽然输出看起来已经很不错了,但还有一些小问题:
- 详细、警告和调试消息没有像往常一样着色。
- 第 2 行中的“文本”一词应为绿色。由于使用
$PSStyle.Reset
,这不起作用。删除后,错误消息的颜色会渗入日期列,看起来更糟。它可以被修复,但它不是微不足道的。
- 换行不正确(换行到输出中间的日期列)。
作为一个更通用、可重用的解决方案,我创建了一个函数Invoke-WithDateLog
,它运行一个脚本块,捕获它的所有输出,在前面插入一个日期每行并再次输出:
Function Invoke-WithDateLog {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[scriptblock] $ScriptBlock,
[Parameter()]
[string] $DateFormat = '[yy\/MM\/dd H:mm:ss] ',
[Parameter()]
[string] $DateStyle = $PSStyle.Foreground.BrightBlack,
[Parameter()]
[switch] $CatchExceptions,
[Parameter()]
[switch] $ExceptionStackTrace,
[Parameter()]
[Collections.ICollection] $ErrorCollection
)
# Variables are private so they are not visible from within the ScriptBlock.
$private:ansiEscapePattern = "`e\[[0-9;]*m"
$private:lastFmt = ''
& {
if( $CatchExceptions ) {
try { & $scriptBlock }
catch {
# The common parameter -ErrorVariable doesn't work in scripted cmdlets, so use our own error variable parameter.
if( $null -ne $ErrorCollection ) {
$null = $ErrorCollection.Add( $_ )
}
# Write as regular output, colored like an error message.
"`n" + $PSStyle.Formatting.Error + "EXCEPTION ($($_.Exception.GetType().FullName)):`n $_" + $PSStyle.Reset
# Optionally write stacktrace. Using the -replace operator we indent each line.
Write-Debug ($_.ScriptStackTrace -replace '^|\r?\n', "`n ") -Debug:$ExceptionStackTrace
}
}
else {
& $scriptBlock
}
} *>&1 | ForEach-Object -PipelineVariable record {
# Here the $_ variable is either:
# - a string in case of simple output
# - an instance of one of the System.Management.Automation.*Record classes (output of Write-Error, Write-Debug, ...)
# - an instance of one of the Microsoft.PowerShell.Commands.Internal.Format.* classes (output of a Format-* cmdlet)
if( $_ -is [System.Management.Automation.ErrorRecord] ) {
# The common parameter -ErrorVariable doesn't work in scripted cmdlets, so use our own error variable parameter.
if( $null -ne $ErrorCollection ) {
$null = $ErrorCollection.Add( $_ )
}
}
$_ # Forward current record
} | Out-String -Stream | ForEach-Object {
# Here the $_ variable is always a (possibly multiline) string of formatted output.
# Out-String doesn't add any ANSI escape codes to colorize Verbose, Warning and Debug messages,
# so we have to do it by ourselfs.
$overrideFmt = switch( $record ) {
{ $_ -is [System.Management.Automation.VerboseRecord] } { $PSStyle.Formatting.Verbose; break }
{ $_ -is [System.Management.Automation.WarningRecord] } { $PSStyle.Formatting.Warning; break }
{ $_ -is [System.Management.Automation.DebugRecord] } { $PSStyle.Formatting.Debug; break }
}
# Prefix for each line. It resets the ANSI escape formatting before the date.
$prefix = $DateStyle + (Get-Date -Format $DateFormat) + $PSStyle.Reset
foreach( $line in $_ -split '\r?\n' ) {
# Produce the final, formatted output.
$prefix + ($overrideFmt ?? $lastFmt) + $line + ($overrideFmt ? $PSStyle.Reset : '')
# Remember last ANSI escape sequence (if any) of current line, for cases where formatting spans multiple lines.
$lastFmt = [regex]::Match( $line, $ansiEscapePattern, 'RightToLeft' ).Value
}
}
}
用法示例:
# To differentiate debug and verbose output from warnings
$PSStyle.Formatting.Debug = $PSStyle.Foreground.Yellow
$PSStyle.Formatting.Verbose = $PSStyle.Foreground.BrightCyan
Invoke-WithDateLog -CatchExceptions -ExceptionStackTrace {
"Write $($PSStyle.Foreground.Green)colored`ntext$($PSStyle.Reset) to stdout"
[PSCustomObject]@{ Answer = 42; Question = 'What?' } | Format-Table
Get-Content -Path not-exists -EA Continue # produce a non-terminating error
Write-Host 'Write to information stream'
Write-Warning 'Write to warning stream'
Write-Verbose 'Write to verbose stream' -Verbose
Write-Debug 'Write to debug stream' -Debug
throw 'Critical error'
}
在 PS 7.2 控制台中输出:
备注:
代码需要PowerShell 7+.
可以通过参数 -DateFormat
更改日期格式(参见 formatting specifiers) and -DateStyle
(ANSI escape sequence for coloring)。
Script-terminating 错误,例如通过抛出异常或使用 Write-Error -EA Stop
创建的错误,不会被 default 记录。相反,它们像往常一样从脚本块中冒出来。您可以传递参数 -CatchExceptions
来捕获异常并像常规 non-terminating 错误一样记录它们。传递 -ExceptionStackTrace
还可以记录脚本堆栈跟踪,这对调试非常有用。
像这样的脚本化 cmdlet 不会设置 automatic variable $?
也不会在出现错误时将错误添加到自动 $Error
变量通过 Write-Error
写入。公共参数 -ErrorVariable
都不起作用。为了仍然能够收集错误信息,我添加了参数 -ErrorCollection
可以像这样使用:
$scriptErrors = [Collections.ArrayList]::new()
Invoke-WithDateLog -CatchExceptions -ExceptionStackTrace -ErrorCollection $scriptErrors {
Write-Error 'Write to stderr' -EA Continue
throw 'Critical error'
}
if( $scriptErrors ) {
# Outputs "Number of errors: 2"
"`nNumber of errors: $($scriptErrors.Count)"
}
当运行脚本为所有日志输出添加日期前缀时,是否可以在 powershell 中使用?
我知道可以这样做: Write-Host "$(Get-Date -format 'u') 我的日志输出"
但我不想每次输出一行时都调用一些函数。相反,我想在 运行 任何脚本或命令时修改所有输出,并为每一行添加时间前缀。
objects generated by Write-Host
already come with a timestamp, you can use Update-TypeData
to override the .ToString()
Method from the InformationRecord
Class and then redirect the output from the Information Stream to the Success Stream.
Update-TypeData -TypeName System.Management.Automation.InformationRecord -Value {
return $this.TimeGenerated.ToString('u') + $this.MessageData.Message.PadLeft(10)
} -MemberType ScriptMethod -MemberName ToString -Force
'Hello', 'World', 123 | Write-Host 6>&1
在all输出前插入一个日期,即stdout,stderr和 PowerShell-specific streams,您可以使用重定向运算符 *>&1
重定向(合并)命令或脚本块的所有流,管道到 Out-String -Stream
以格式化流 objects 到 text 行,然后使用 ForEach-Object
处理每一行并在前面加上日期。
让我从一个简单的例子开始,可以在下面找到更完整的解决方案。
# Run a scriptblock
&{
# Test output to all possible streams, using various formatting methods.
# Added a few delays to test if the final output is still streaming.
"Write $($PSStyle.Foreground.BrightGreen)colored`ntext$($PSStyle.Reset) to stdout"
Start-Sleep -Millis 250
[PSCustomObject]@{ Answer = 42; Question = 'What?' } | Format-Table
Start-Sleep -Millis 250
Get-Content -Path not-exists -EA Continue # produce a non-terminating error
Start-Sleep -Millis 250
Write-Host 'Write to information stream'
Start-Sleep -Millis 250
Write-Warning 'Write to warning stream'
Start-Sleep -Millis 250
Write-Verbose 'Write to verbose stream' -Verbose
Start-Sleep -Millis 250
$DebugPreference = 'Continue' # To avoid prompt, needed for Windows Powershell
Write-Debug 'Write to debug stream'
} *>&1 | Out-String -Stream | ForEach-Object {
# Add date in front of each output line
$date = Get-Date -Format "yy\/MM\/dd H:mm:ss"
foreach( $line in $_ -split '\r?\n' ) {
"$($PSStyle.Reset)[$date] $line"
}
}
在 PS 7.2 控制台中输出:
- 使用
Out-String
我们使用标准的 PowerShell 格式化系统使输出看起来正常,因为它会在没有重定向的情况下出现(例如,表格之类的东西保持不变)。-Stream
参数对于保持 PowerShell 的 streaming 输出行为至关重要。如果没有此参数,只有在整个脚本块完成后才会收到输出。 - 虽然输出看起来已经很不错了,但还有一些小问题:
- 详细、警告和调试消息没有像往常一样着色。
- 第 2 行中的“文本”一词应为绿色。由于使用
$PSStyle.Reset
,这不起作用。删除后,错误消息的颜色会渗入日期列,看起来更糟。它可以被修复,但它不是微不足道的。 - 换行不正确(换行到输出中间的日期列)。
作为一个更通用、可重用的解决方案,我创建了一个函数Invoke-WithDateLog
,它运行一个脚本块,捕获它的所有输出,在前面插入一个日期每行并再次输出:
Function Invoke-WithDateLog {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[scriptblock] $ScriptBlock,
[Parameter()]
[string] $DateFormat = '[yy\/MM\/dd H:mm:ss] ',
[Parameter()]
[string] $DateStyle = $PSStyle.Foreground.BrightBlack,
[Parameter()]
[switch] $CatchExceptions,
[Parameter()]
[switch] $ExceptionStackTrace,
[Parameter()]
[Collections.ICollection] $ErrorCollection
)
# Variables are private so they are not visible from within the ScriptBlock.
$private:ansiEscapePattern = "`e\[[0-9;]*m"
$private:lastFmt = ''
& {
if( $CatchExceptions ) {
try { & $scriptBlock }
catch {
# The common parameter -ErrorVariable doesn't work in scripted cmdlets, so use our own error variable parameter.
if( $null -ne $ErrorCollection ) {
$null = $ErrorCollection.Add( $_ )
}
# Write as regular output, colored like an error message.
"`n" + $PSStyle.Formatting.Error + "EXCEPTION ($($_.Exception.GetType().FullName)):`n $_" + $PSStyle.Reset
# Optionally write stacktrace. Using the -replace operator we indent each line.
Write-Debug ($_.ScriptStackTrace -replace '^|\r?\n', "`n ") -Debug:$ExceptionStackTrace
}
}
else {
& $scriptBlock
}
} *>&1 | ForEach-Object -PipelineVariable record {
# Here the $_ variable is either:
# - a string in case of simple output
# - an instance of one of the System.Management.Automation.*Record classes (output of Write-Error, Write-Debug, ...)
# - an instance of one of the Microsoft.PowerShell.Commands.Internal.Format.* classes (output of a Format-* cmdlet)
if( $_ -is [System.Management.Automation.ErrorRecord] ) {
# The common parameter -ErrorVariable doesn't work in scripted cmdlets, so use our own error variable parameter.
if( $null -ne $ErrorCollection ) {
$null = $ErrorCollection.Add( $_ )
}
}
$_ # Forward current record
} | Out-String -Stream | ForEach-Object {
# Here the $_ variable is always a (possibly multiline) string of formatted output.
# Out-String doesn't add any ANSI escape codes to colorize Verbose, Warning and Debug messages,
# so we have to do it by ourselfs.
$overrideFmt = switch( $record ) {
{ $_ -is [System.Management.Automation.VerboseRecord] } { $PSStyle.Formatting.Verbose; break }
{ $_ -is [System.Management.Automation.WarningRecord] } { $PSStyle.Formatting.Warning; break }
{ $_ -is [System.Management.Automation.DebugRecord] } { $PSStyle.Formatting.Debug; break }
}
# Prefix for each line. It resets the ANSI escape formatting before the date.
$prefix = $DateStyle + (Get-Date -Format $DateFormat) + $PSStyle.Reset
foreach( $line in $_ -split '\r?\n' ) {
# Produce the final, formatted output.
$prefix + ($overrideFmt ?? $lastFmt) + $line + ($overrideFmt ? $PSStyle.Reset : '')
# Remember last ANSI escape sequence (if any) of current line, for cases where formatting spans multiple lines.
$lastFmt = [regex]::Match( $line, $ansiEscapePattern, 'RightToLeft' ).Value
}
}
}
用法示例:
# To differentiate debug and verbose output from warnings
$PSStyle.Formatting.Debug = $PSStyle.Foreground.Yellow
$PSStyle.Formatting.Verbose = $PSStyle.Foreground.BrightCyan
Invoke-WithDateLog -CatchExceptions -ExceptionStackTrace {
"Write $($PSStyle.Foreground.Green)colored`ntext$($PSStyle.Reset) to stdout"
[PSCustomObject]@{ Answer = 42; Question = 'What?' } | Format-Table
Get-Content -Path not-exists -EA Continue # produce a non-terminating error
Write-Host 'Write to information stream'
Write-Warning 'Write to warning stream'
Write-Verbose 'Write to verbose stream' -Verbose
Write-Debug 'Write to debug stream' -Debug
throw 'Critical error'
}
在 PS 7.2 控制台中输出:
备注:
代码需要PowerShell 7+.
可以通过参数
-DateFormat
更改日期格式(参见 formatting specifiers) and-DateStyle
(ANSI escape sequence for coloring)。Script-terminating 错误,例如通过抛出异常或使用
Write-Error -EA Stop
创建的错误,不会被 default 记录。相反,它们像往常一样从脚本块中冒出来。您可以传递参数-CatchExceptions
来捕获异常并像常规 non-terminating 错误一样记录它们。传递-ExceptionStackTrace
还可以记录脚本堆栈跟踪,这对调试非常有用。像这样的脚本化 cmdlet 不会设置 automatic variable
$?
也不会在出现错误时将错误添加到自动$Error
变量通过Write-Error
写入。公共参数-ErrorVariable
都不起作用。为了仍然能够收集错误信息,我添加了参数-ErrorCollection
可以像这样使用:$scriptErrors = [Collections.ArrayList]::new() Invoke-WithDateLog -CatchExceptions -ExceptionStackTrace -ErrorCollection $scriptErrors { Write-Error 'Write to stderr' -EA Continue throw 'Critical error' } if( $scriptErrors ) { # Outputs "Number of errors: 2" "`nNumber of errors: $($scriptErrors.Count)" }