2012-02-23 11 views
33

Modifica: Soluzione in fondo a questo post.Powershell: colorazione corretta dell'output Get-Childitem una volta per tutte

Colorare Get-Childitem (dir o ls, in altre parole) non è una nuova idea esattamente, ma non sono stato in grado di individuare eventuali approcci ideali per colorare uscita in PowerShell. Ci sono due approcci generali per la scrittura di funzioni colore-LS:

  • Intercettare output di Get-ChildItem, e ri-output come testo utilizzando Write-Host con il parametro -foregroundcolor. Questo approccio consente la massima granularità possibile, ma riduce l'output di Get-Childitem al testo. Come molti utenti di PowerShell sono a conoscenza, Get-Childitem non emette il testo, ma emette oggetti. In particolare, un elenco di oggetti FileInfo e DirectoryInfo. Ciò consente una grande flessibilità nella gestione dell'output Get-Childitem.

  • Eseguire il pipe dell'output di Get-Childitem tramite Invoke-Expression su Foreach-Object, cambiando il colore di primo piano della console prima di emettere ogni oggetto. Una specie di boccone, ma l'opzione migliore perché conserva il tipo di output di Get-Childitem.

Ecco un esempio di quest'ultimo approccio, fornito da Tim Johnson's Powershell Blog.

function color-ls 
{ 
    $regex_opts = ([System.Text.RegularExpressions.RegexOptions]::IgnoreCase ` 
      -bor [System.Text.RegularExpressions.RegexOptions]::Compiled) 
    $fore = $Host.UI.RawUI.ForegroundColor 
    $compressed = New-Object System.Text.RegularExpressions.Regex(
      '\.(zip|tar|gz|rar|jar|war)$', $regex_opts) 
    $executable = New-Object System.Text.RegularExpressions.Regex(
      '\.(exe|bat|cmd|py|pl|ps1|psm1|vbs|rb|reg)$', $regex_opts) 
    $text_files = New-Object System.Text.RegularExpressions.Regex(
      '\.(txt|cfg|conf|ini|csv|log|xml|java|c|cpp|cs)$', $regex_opts) 

    Invoke-Expression ("Get-ChildItem $args") | ForEach-Object { 
     if ($_.GetType().Name -eq 'DirectoryInfo') 
     { 
      $Host.UI.RawUI.ForegroundColor = 'Magenta' 
      echo $_ 
      $Host.UI.RawUI.ForegroundColor = $fore 
     } 
     elseif ($compressed.IsMatch($_.Name)) 
     { 
      $Host.UI.RawUI.ForegroundColor = 'darkgreen' 
      echo $_ 
      $Host.UI.RawUI.ForegroundColor = $fore 
     } 
     elseif ($executable.IsMatch($_.Name)) 
     { 
      $Host.UI.RawUI.ForegroundColor = 'Red' 
      echo $_ 
      $Host.UI.RawUI.ForegroundColor = $fore 
     } 
     elseif ($text_files.IsMatch($_.Name)) 
     { 
      $Host.UI.RawUI.ForegroundColor = 'Yellow' 
      echo $_ 
      $Host.UI.RawUI.ForegroundColor = $fore 
     } 
     else 
     { 
      echo $_ 
     } 
    } 
} 

Questo codice assegna colori differenti basati esclusivamente sulla estensione, ma quasi qualsiasi metrica potrebbe essere sostituito per differenziare i tipi di file. Il codice precedente produce il seguente output:

Colored get-childitem example

Questo è quasi perfetto, ma c'è un piccolo difetto: la prima uscita 3 linee (percorso Directory, intestazione colonne, separatori orizzontali) assumere il colore del primo elemento nell'elenco. Tim Johnson ha commentato nel suo blog:

Preferirei se l'intestazione nella parte superiore non fosse sempre dello stesso colore del primo elemento, ma non riesco a pensare in alcun modo a tale proposito.

Né posso, sfortunatamente. Ecco dove arrivano Stack Overflow e i suoi guru PowerShell: sto cercando un modo per colorare l'output di Get-Childitem preservando il tipo di output del cmdlet, senza compromettere il colore dell'intestazione. Ho fatto qualche esperimento e ho giocato con questo approccio, ma non ho ancora avuto alcun successo, dato che la prima singola chiamata echo emette l'intera intestazione e il primo elemento.

Tutte le domande, i commenti o, meglio ancora, le soluzioni sono benvenute.

La soluzione Con grazie a Jon Z e gli altri che hanno fornito idee:

Jon Z fornito la soluzione perfetta a questo problema, che ho ripulito un po 'per abbinare il regime in mia domanda iniziale. Eccolo, per chiunque sia interessato. Si noti che questo richiede New-CommandWrapper cmdlet dal libro di cucina PowerShell. Tutto questo codice va nel tuo profilo.

function Write-Color-LS 
    { 
     param ([string]$color = "white", $file) 
     Write-host ("{0,-7} {1,25} {2,10} {3}" -f $file.mode, ([String]::Format("{0,10} {1,8}", $file.LastWriteTime.ToString("d"), $file.LastWriteTime.ToString("t"))), $file.length, $file.name) -foregroundcolor $color 
    } 

New-CommandWrapper Out-Default -Process { 
    $regex_opts = ([System.Text.RegularExpressions.RegexOptions]::IgnoreCase) 


    $compressed = New-Object System.Text.RegularExpressions.Regex(
     '\.(zip|tar|gz|rar|jar|war)$', $regex_opts) 
    $executable = New-Object System.Text.RegularExpressions.Regex(
     '\.(exe|bat|cmd|py|pl|ps1|psm1|vbs|rb|reg)$', $regex_opts) 
    $text_files = New-Object System.Text.RegularExpressions.Regex(
     '\.(txt|cfg|conf|ini|csv|log|xml|java|c|cpp|cs)$', $regex_opts) 

    if(($_ -is [System.IO.DirectoryInfo]) -or ($_ -is [System.IO.FileInfo])) 
    { 
     if(-not ($notfirst)) 
     { 
      Write-Host 
      Write-Host " Directory: " -noNewLine 
      Write-Host " $(pwd)`n" -foregroundcolor "Magenta"   
      Write-Host "Mode    LastWriteTime  Length Name" 
      Write-Host "----    -------------  ------ ----" 
      $notfirst=$true 
     } 

     if ($_ -is [System.IO.DirectoryInfo]) 
     { 
      Write-Color-LS "Magenta" $_     
     } 
     elseif ($compressed.IsMatch($_.Name)) 
     { 
      Write-Color-LS "DarkGreen" $_ 
     } 
     elseif ($executable.IsMatch($_.Name)) 
     { 
      Write-Color-LS "Red" $_ 
     } 
     elseif ($text_files.IsMatch($_.Name)) 
     { 
      Write-Color-LS "Yellow" $_ 
     } 
     else 
     { 
      Write-Color-LS "White" $_ 
     } 

    $_ = $null 
    } 
} -end { 
    write-host "" 
} 

Questo produce un output che appare simile al seguente screenshot: enter image description here

Se si desidera che la linea di totale dimensioni del file in fondo, è sufficiente aggiungere il seguente codice:

Remove-Item alias:ls 
Set-Alias ls LS-Padded 

function LS-Padded 
{ 
    param ($dir) 
    Get-Childitem $dir 
    Write-Host 
    getDirSize $dir 
} 

function getDirSize 
{ 
    param ($dir) 
    $bytes = 0 

    Get-Childitem $dir | foreach-object { 

     if ($_ -is [System.IO.FileInfo]) 
     { 
      $bytes += $_.Length 
     } 
    } 

    if ($bytes -ge 1KB -and $bytes -lt 1MB) 
    { 
     Write-Host ("Total Size: " + [Math]::Round(($bytes/1KB), 2) + " KB") 
    } 

    elseif ($bytes -ge 1MB -and $bytes -lt 1GB) 
    { 
     Write-Host ("Total Size: " + [Math]::Round(($bytes/1MB), 2) + " MB") 
    } 

    elseif ($bytes -ge 1GB) 
    { 
     Write-Host ("Total Size: " + [Math]::Round(($bytes/1GB), 2) + " GB") 
    }  

    else 
    { 
     Write-Host ("Total Size: " + $bytes + " bytes") 
    } 
} 
+0

Ciao. Ecco alcuni link rilevanti: http://stackoverflow.com/a/8088587/539542 – Animesh

+0

L'intestazione e il separatore non sono un output di get-childitem. Si tratta solo di informazioni aggiuntive quando viene visualizzato un filesystemobject. Quindi probabilmente dovresti modificare FileSystem.format.ps1xml ... se è anche possibile – Tom

+0

la soluzione di Jon è l'ideale. Una volta scritta una nuova versione ben collaudata e lucidata, la inserirò nell'OP per riferimenti futuri. – Fopedush

risposta

14

Modificare l'impostazione predefinita è sicuramente la soluzione. Sotto un esempio - scontato, sciatto. Sto usando New-CommandWrapper dal ricettario di PowerShell.

New-CommandWrapper Out-Default ` 
    -Process { 
     if(($_ -is [System.IO.DirectoryInfo]) -or ($_ -is [System.IO.FileInfo])) 
     {if(-not ($notfirst)) { 
      Write-Host " Directory: $(pwd)`n"   
      Write-Host "Mode    LastWriteTime  Length Name" 
      Write-Host "----    -------------  ------ ----" 
      $notfirst=$true 
      } 
      if ($_ -is [System.IO.DirectoryInfo]) { 
      Write-host ("{0,-7} {1,25} {2,10} {3}" -f $_.mode, ([String]::Format("{0,10} {1,8}", $_.LastWriteTime.ToString("d"), $_.LastWriteTime.ToString("t"))), $_.length, $_.name) -foregroundcolor "yellow" } 
      else { 
      Write-host ("{0,-7} {1,25} {2,10} {3}" -f $_.mode, ([String]::Format("{0,10} {1,8}", $_.LastWriteTime.ToString("d"), $_.LastWriteTime.ToString("t"))), $_.length, $_.name) -foregroundcolor "green" } 
      $_ = $null 
     } 
} 

Example Directory Listing

+0

Sembra la risposta. Controllerò una volta che avrò avuto la possibilità di giocarci un po '. – Fopedush

+0

Sì, questa è sicuramente la risposta. Ho alcuni problemi di formattazione per gestire no, ma nulla che non possa gestire. Grazie! – Fopedush

+0

Ecco un pensiero: esiste un modo semplice per aggiungere una nuova riga alla fine dell'output? (o, per esempio, aggiungi una fila di filesize totale in fondo?) Non so come controllare in modo programmatico se una voce è l'ultima. EDIT: Suppongo che potrei semplicemente scrivere una funzione che chiama LS, quindi scrive una nuova riga e alias quella funzione. No biggie. – Fopedush

3

Forse tramite una funzione proxy Out-Default.

+0

Wrapping Out-Default può dare una soluzione più ampia, non ho provato da solo ma sembra il candidato giusto –

+0

Ho appena creato lo stub per Out-Default e ho giocato con un po '. La prima volta che gioco con i proxy :-) Ho imparato che '$ scriptCmd' nel blocco di partenza deve essere un singolo comando, quindi non è consentita la logica. La logica è consentita nel blocco di processo. Ho aggiunto '$ host.ui.rawui.foregroundground' al blocco del processo ma ha lo stesso problema in cui' Directory: 'nella parte superiore e le intestazioni delle colonne sono dello stesso colore delle directory.'Write-Host $ _ -Fore Green' nel blocco di processo aggiunge solo righe extra nell'output. La prima volta a giocare con questo però ... Ancora imparando ... –

+0

@AndyArismendi - dai uno sguardo: http://dmitrysotnikov.files.wordpress.com/2011/09/scripts-tec-2011-jeffrey-snover-proxy -functions.pdf –

0

Ho un'altra soluzione. Puoi semplicemente avere un formato .format.ps1xml personalizzato e apportare alcune modifiche per rendere possibile la colorazione.

ho la mia persona di file .format.PS1XML formattazione su github.com: https://github.com/ecsousa/PSUtils/blob/master/CustomPSUtils.format.ps1xml

Per utilizzarlo, tutto quello che dovete fare è:

Update-FormatData -Prepend CustomPSUtils.format.ps1xml 

Inoltre, per essere sicuri di tornare indietro al colore della console originale dopo un oggetto Get-Child, è necessario sostituire la funzione prompt. Qualcosa di simile a questo:

function prompt { 
    if($global:FSFormatDefaultColor) { 
     [Console]::ForegroundColor = $global:FSFormatDefaultColor 
    } 

    "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) " 
} 
12

Ho appena installato e utilizzato https://github.com/Davlind/PSColor, che è stato indolore. Supporta PSGet in modo da poter installare facilmente con Install-Module PSColor per ottenerlo.

Gli oggetti non vengono trasformati in modo da supportare ancora le tubazioni. (Sta usando il New-CommandWrapper menzionato sopra)

Supporta anche altre cose come select-string.

PowerShell Color

+2

Probabilmente questa dovrebbe essere la risposta accettata. A differenza degli altri qui (non ho provato joon's) per me funziona solo senza modifiche (ottengo più errori di sintassi per tutte le altre risposte) e non rallenta terribilmente ls. – stijn

Problemi correlati