2011-09-09 10 views
7

È possibile colorare solo determinate parole (non righe complete) per un output di PowerShell utilizzando la tabella di formato. Ad esempio, questo script esegue la scansione di una cartella in modo ricorsivo per una stringa e quindi genera il risultato con la tabella di formato.Parole di colore in formato script di PowerShell-output tabella

dir -r -i *.* | Select-String $args[0] | 
format-table -Property @{label="Line #"; Expression={$_.LineNumber}; width=6}, 
Path, Line -wrap 

Sarebbe bello essere in grado di formattare la parola che stiamo cercando con un colore specifico, in modo da poter vedere esattamente dove è stato trovato sulla linea.

risposta

14

È possibile reindirizzare il tavolo su Out-String, quindi scrivere la stringa in parti utilizzando Write-Host con lo switch -NoNewLine.

Qualcosa di simile a questo:

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 
+2

funziona benissimo! Modificato questa linea: $ index = $ line.IndexOf ($ word) a $ index = $ line.IndexOf ($ parola, [System.StringComparison] :: InvariantCultureIgnoreCase) – EtienneT

+0

Buon punto sul caso ignorare. Ho aggiornato la risposta con la modifica. – Rynant

5

Mi piace Rynant s' approccio. Ecco un'implementazione alternativa, utilizzando -split anziché 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 include stringhe vuote se la linea inizia o termina con la parola data, quindi l'extra "se non prima" logica.


Edit: seguito il commento di Rynant, ecco un'altra applicazione che supporta sia semplici e regex modelli:

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 
} 

L'uscita dai seguenti esempi mostra la differenza:

PS> '\d00\d!' | ColorPattern '\d' 'Magenta' -Simple
\d00\d!

PS> '\d00\d!' | ColorPattern '\d' 'Magenta'
\d00\d!

+1

Basta fare attenzione che è necessario sfuggire ai caratteri di regex. Per esempio. ''Ke $ ha - Tik Tok.mp3' | 'Ke \ $ ha' di ColorWords rosso. In alternativa, '$ _ -split [regex] :: Escape ($ word)' potrebbe essere usato nel filtro.A meno che tu non voglia abbinarti alla regex, che potrebbe essere carina. – Rynant

+0

@Rynant Grazie per avermelo fatto notare! Aggiornato con la nuova versione. –

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 

un po 'tardi per la risposta, ma ho aggiornato questo con il supporto regex multiplo, così come semplice corrispondenza. Questo è stato testato in Powershell v4.0.

3

Mi piace rispondere @Ryant ha dato. Ho qui una versione modificata che può essere usata per colorare più parole in un'uscita passando per matrici o parole e colori. Il trucco è che devi dividere il testo di input in linee basate sul separatore di nuova riga.

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 
} } 

e sarebbero stati giustiziati come segue

Get-Service | Format-Table| Out-String| ColorWord2 -word 'Running','Stopped' -color 'Green','Red' 
+0

Amo questa risposta, è esattamente quello che stavo cercando. – sch4v4

+0

Cheers. Attualmente uso una variazione su questo. Invece di passare in due matrici, passo semplicemente in una tabella hash, con il colore come chiave e il valore come una serie di parole a cui verrà applicato il colore. – stevethethread

+0

@stevethethread \t Puoi condividere la tua ultima versione? –

Problemi correlati