是否可以使用format-table为powershell输出仅为某些单词(不是完整的行)着色。例如,此脚本以递归方式扫描文件夹中的字符串,然后使用format-table输出结果。
dir -r -i *.* | Select-String $args[0] |
format-table -Property @{label="Line #"; Expression={$_.LineNumber}; width=6},
Path, Line -wrap
能够使用特定颜色格式化我们正在搜索的单词会很好,这样您就可以准确地看到它在线上的位置。
答案 0 :(得分:14)
您可以将表格导入Out-String
,然后使用带有Write-Host
开关的-NoNewLine
将字符串分成几部分。
这样的事情:
filter ColorWord {
param(
[string] $word,
[string] $color
)
$line = $_
$index = $line.IndexOf($word, [System.StringComparison]::InvariantCultureIgnoreCase)
while($index -ge 0){
Write-Host $line.Substring(0,$index) -NoNewline
Write-Host $line.Substring($index, $word.Length) -NoNewline -ForegroundColor $color
$used = $word.Length + $index
$remain = $line.Length - $used
$line = $line.Substring($used, $remain)
$index = $line.IndexOf($word, [System.StringComparison]::InvariantCultureIgnoreCase)
}
Write-Host $line
}
Get-Process| Format-Table| Out-String| ColorWord -word 1 -color magenta
答案 1 :(得分:5)
我喜欢Rynant的方法。以下是使用-split
代替IndexOf
的替代实现:
filter ColorWord( [string]$word, [ConsoleColor]$color ) {
$later = $false
$_ -split [regex]::Escape( $word ) | foreach {
if( $later ) { Write-Host "$word" -NoNewline -ForegroundColor $color }
else { $later = $true }
Write-Host $_ -NoNewline
}
Write-Host
}
如果行以给定的单词开头或结尾,则Split包含空字符串,因此额外的“if not first”逻辑。
编辑:根据Rynant的评论,这是另一个支持简单和正则表达式模式的实现:
filter ColorPattern( [string]$Pattern, [ConsoleColor]$Color, [switch]$SimpleMatch ) {
if( $SimpleMatch ) { $Pattern = [regex]::Escape( $Pattern ) }
$split = $_ -split $Pattern
$found = [regex]::Matches( $_, $Pattern, 'IgnoreCase' )
for( $i = 0; $i -lt $split.Count; ++$i ) {
Write-Host $split[$i] -NoNewline
Write-Host $found[$i] -NoNewline -ForegroundColor $Color
}
Write-Host
}
以下示例的输出显示了差异:
PS> '\d00\d!' | ColorPattern '\d' 'Magenta' -Simple
\d00\d!
PS> '\d00\d!' | ColorPattern '\d' 'Magenta'
\d00\d!
答案 2 :(得分:4)
我喜欢回答@Ryant的回答。我在这里有一个修改版本,可用于通过传入数组或单词和颜色来着色输出中的多个单词。诀窍是你必须根据换行符分隔符将输入文本拆分成行。
filter ColorWord2 {
param(
[string[]] $word,
[string[]] $color
)
$all = $_
$lines = ($_ -split '\r\n')
$lines | % {
$line = $_
$x = -1
$word | % {
$x++
$item = $_
$index = $line.IndexOf($item, [System.StringComparison]::InvariantCultureIgnoreCase)
while($index -ge 0){
Write-Host $line.Substring(0,$index) -NoNewline
Write-Host $line.Substring($index, $item.Length) -NoNewline -ForegroundColor $color[$x]
$used =$item.Length + $index
$remain = $line.Length - $used
$line =$line.Substring($used, $remain)
$index = $line.IndexOf($item, [System.StringComparison]::InvariantCultureIgnoreCase)
}
}
Write-Host $line
} }
并将执行如下
Get-Service | Format-Table| Out-String| ColorWord2 -word 'Running','Stopped' -color 'Green','Red'
答案 3 :(得分:1)
#$VerbosePreference = 'continue'
$VerbosePreference = 'silent'
filter ColorPattern {
param ([object]$colors, [switch]$SimpleMatch)
[string]$line = $_
$collection = New-Object 'System.Collections.Generic.SortedDictionary[int, pscustomobject]'
$RegexOptions = [Text.RegularExpressions.RegexOptions]::IgnoreCase.value__ + [Text.RegularExpressions.RegexOptions]::Singleline.value__
if ($SimpleMatch){
$patternMatches = $colors.keys | % {[regex]::Escape($_)}
$reference = 'Value'
} else {
$patternMatches = $colors.keys
$reference = 'Pattern'
}
# detect RegEx matches and add to collection object
Write-Verbose "'$line'"
$measureparsing_match = (Measure-Command {
foreach ($pattern in $patternMatches){
Write-Verbose "regex pattern: $pattern"
foreach ($match in ([regex]::Matches($line, $pattern, $RegexOptions))){ # lazy matching
Write-Verbose "`tmatch index: $($match.Index) length: $($match.length)"
$currentset = ($match.Index)..($match.Index + $match.length - 1)
Write-Verbose "`tcurrent set: $currentset"
if (-not [bool]$collection.Count){
Write-Verbose "`t`tindex: $($match.Index) value: $($match.value) (inital add)"
[void]$collection.Add($match.Index, [PSCustomObject]@{Length = $match.Length; Value = $match.Value; Pattern = $pattern; Range = $currentset})
} else {
(,$collection.Values) | % {
$currentRange = $_.range
$intersect = Compare-Object -PassThru $currentset $currentRange -IncludeEqual -ExcludeDifferent
if ($intersect){
Write-Verbose "`t`tintersect: $([string]($intersect | % {[string]::Concat($_)})) (skipped)"
$nonintersect = Compare-Object -PassThru $currentset $intersect
Write-Verbose "`t`tnonintersect: $([string]($nonintersect | % {[string]::Concat($_)}))"
$nonintersect | % {
if ($currentRange -notcontains $_){
Write-Verbose "`t`tindex: $_ value: $($line[$_]) (adding intersect-fallout)"
[void]$collection.Add($_, [PSCustomObject]@{Length = $_.Length; Value = $line[$_]; Pattern = $pattern; Range = $currentset})
} else {
Write-Verbose "`t`t`tindex: $_ value: $($line[$_]) (skipped intersect-fallout)"
}
}
} else {
Write-Verbose "`t`tindex: $($match.index) value: $($match.value) (adding nonintersect)"
[void]$collection.Add($match.Index, [PSCustomObject]@{Length = $match.Length; Value = $match.Value; Pattern = $pattern; Range = $currentset})
}
} # end values
} #end if
} # end matching
} # end pattern
}).TotalMilliseconds
$measureparsing_nonmatch = (Measure-Command {
if ([bool]$collection.count){ # if there are no matches, skip!
Compare-Object -PassThru `
-ReferenceObject (
$collection.Keys | % { # all matched keys and their lengths
$word = $collection.item($_)
$currentlength = ($word.value).length
($_..($_ + ($currentlength - 1)))
}) `
-DifferenceObject (0..($line.Length - 1)) | # entire line
% {[void]$collection.Add($_, [PSCustomObject]@{Length = $_.length; Value = $line[$_]})} # add non matches to collection
}
}).TotalMilliseconds
Write-Verbose "match: $measureparsing_match ms. VS nonmatch: $measureparsing_nonmatch ms."
$collection.keys | % {
$word = $collection.item($_)
if ($word.pattern){
if ($colors.ContainsKey($word.$reference)){
$color = @{
ForegroundColor = $colors[$word.$reference].ForegroundColor;
BackgroundColor = $colors[$word.$reference].BackgroundColor
}
if ($word.value){
Write-Host -NoNewline $([string]::Concat($word.value)) @color
}
}
} else {
Write-Host -NoNewline $([string]::Concat($word.value))
}
}
Write-Host # needed for line feed
}
$Patterns = [ordered]@{
# higher in list takes precendence
'stopped' = @{ForegroundColor = 'Red'; BackgroundColor='DarkRed'}
'running' = @{ForegroundColor = 'Green'; BackgroundColor='DarkGreen'}
'paused' = @{ForegroundColor = 'Yellow'; BackgroundColor='DarkYellow'}
0 = @{ForegroundColor = 'White'; BackgroundColor='Gray'}
'\d+' = @{ForegroundColor = 'Gray'; BackgroundColor='Black'}
'\.' = @{ForegroundColor = 'Magenta'; BackgroundColor='DarkMagenta'}
'(a|e|i|o|u)' = @{ForegroundColor = 'Yellow'; BackgroundColor='DarkYellow'}
'\w+' = @{ForegroundColor = 'Cyan'; BackgroundColor='DarkCyan'}
}
# strongly typed collection.. we could probably do this better..
$colorCollection = New-Object 'system.collections.generic.dictionary[string, hashtable]'([StringComparer]::OrdinalIgnoreCase) # Ordinal
$Patterns.GetEnumerator() | % {[void]$colorCollection.Add($_.Name, $_.Value)}
Get-Service | Out-String -Stream | ColorPattern -colors $colorCollection
#Get-Service | Out-String -Stream | ColorPattern -colors $colorCollection -SimpleMatch
回复迟到了,但我已经通过多个正则表达式支持以及简单匹配更新了这个。这是在Powershell v4.0下测试的。