2012-02-23 7 views
33

Edytuj: Rozwiązanie na dole tego wpisu.Powershell: Prawidłowe wybarwienie wyjścia Get-Childitem raz na zawsze

Kolorowanie Get-ChildItem (reż lub ls, innymi słowy) nie jest dokładnie to nowy pomysł, ale nie byłem w stanie znaleźć żadnych idealne podejście do kolorowania wyjście w PowerShell. Istnieją dwa ogólne podejścia do pisania funkcji Color-LS:

  • Przechwytywanie wyjście Get-ChildItem i ponowne wpisywanie tekstu przy użyciu go jako Write-Host z parametrem -ForegroundColor. Takie podejście pozwala na uzyskanie jak największej ziarnistości, ale zmniejsza wydajność Get-Childitem do tekstu. Ponieważ większość użytkowników programu powershell jest świadoma, Get-Childitem nie generuje tekstu, a raczej generuje obiekty. W szczególności lista obiektów FileInfo i DirectoryInfo. Pozwala to na dużą elastyczność w obsłudze danych wyjściowych Get-Childitem.

  • Wyprowadzanie danych wyjściowych Get-Childitem za pomocą Invoke-Expression do Foreach-Object, zmieniając kolor pierwszego planu konsoli przed wyprowadzeniem każdego obiektu. Coś w rodzaju pełnej, ale lepszej opcji, ponieważ zachowuje typ wyników Get-Childitem.

Oto przykład tej ostatniej metody, dostarczone przez Tim Johnson's Powershell Blog.

Ten kod przypisuje różne kolory w zależności od rozszerzenia pliku, ale w celu rozróżnienia typów plików można zastosować prawie dowolne dane. Powyższy kod wygeneruje następujące wyniki:

Colored get-childitem example

To jest prawie idealna, ale jest jedna mała wada: pierwsze wyjście 3 linie (ścieżka do katalogu, nagłówki kolumny, a separatory poziomy) Wciel się w kolor pierwszego elementu na liście. Tim Johnson skomentował na swoim blogu:

Wolałbym, jeśli nagłówek u góry nie zawsze byłby tego samego koloru co pierwszy przedmiot, ale nie potrafię wymyślić tego.

Nie mogę też niestety. Tam wkracza Stack Overflow i jego guru PowerShell: Szukam sposobu na kolorowanie danych wyjściowych Get-Childitem, przy jednoczesnym zachowaniu wyjściowego typu cmdletu, bez zepsucia koloru nagłówka. Zrobiłem eksperymenty i bawiłem się tym podejściem, ale jeszcze nie osiągnąłem żadnego sukcesu, ponieważ pierwsze pojedyncze wywołanie echa wysyła cały nagłówek i pierwszy element.

Wszelkie pytania, uwagi lub nawet lepsze rozwiązania są mile widziane.

Rozwiązanie z podziękowaniem Jon oo i inni, którzy przewidziane pomysły:

Jon Z warunkiem idealne rozwiązanie tego problemu, który mam polerowanego się trochę dopasować schemat w moim oryginalne pytanie. Oto, dla każdego, kto jest zainteresowany. Zauważ, że wymaga to New-CommandWrapper cmdlet z książki kucharskiej Powershell. Cały ten kod trafia do twojego profilu.

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

To daje wyjście, które wygląda następująco zrzucie: enter image description here

Jeśli chciałbyś całkowitą linię rozmiar pliku na dole, po prostu dodaj następujący kod:

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

Witam. Oto kilka istotnych odnośników: http://stackoverflow.com/a/8088587/539542 – Animesh

+0

Nagłówek i separator nie są wynikiem działania get-childitem. Jest to tylko dodatkowa informacja, gdy wyświetlany jest obiekt systemu plików. Więc prawdopodobnie musisz edytować FileSystem.format.ps1xml ... jeśli to nawet możliwe – Tom

+0

Jon's solution is ideal. Gdy otrzymam wypróbowaną i wypolerowaną nową wersję, umieszczę ją w OP na przyszłość. – Fopedush

Odpowiedz

14

Modyfikowanie Out-Default jest zdecydowanie najlepszym sposobem. Poniżej - przyznany, niechlujny - przykład. Używam New-CommandWrapper z książki kucharskiej 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

To wygląda na odpowiedź. Sprawdzę, gdy tylko będę miał okazję się z nim trochę pograć. – Fopedush

+0

Tak, to zdecydowanie odpowiedź. Mam problemy z formatowaniem, aby poradzić sobie z nie, ale nie mogę sobie z tym poradzić. Dzięki! – Fopedush

+0

Oto myśl - czy istnieje jakiś prosty sposób dodania znaku nowej linii na końcu pliku wyjściowego? (lub, na przykład, dodać cały wiersz filesize na dole?) Nie wiem, jak programowo sprawdzić, czy wpis jest ostatnim. EDIT: Przypuszczam, że mógłbym po prostu napisać funkcję, która wywołuje LS, a następnie pisze znak nowej linii i alias, który działa. Bez wielkiej wagi. – Fopedush

3

Może za pośrednictwem funkcji Out-Default proxy.

+0

Wrapping Out-Default może dać szersze rozwiązanie, nie próbowałem go sam, ale wygląda właściwy kandydat. –

+0

Właśnie utworzyłem skrót do Out-Default i grałem trochę. Pierwszy raz gra z serwerami proxy :-) Dowiedziałem się, że '$ scriptCmd' w bloku początkowym musi być pojedynczym poleceniem, więc nie jest dozwolona żadna logika. Logika jest dozwolona w bloku procesowym. Dodałem '$ host.ui.rawui.foregroundground' do bloku procesu, ale ma ten sam problem, w którym' Directory: 'na górze i nagłówki kolumn są tego samego koloru co katalogi.'Write-Host $ _ -Fore Green' w bloku procesu dodaje dodatkowe linie na wyjściu. Jednak po raz pierwszy bawiąc się tymi ... Ciągle się uczę ... –

+0

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

0

Mam inne rozwiązanie. Możesz po prostu mieć dla niego niestandardowy .format.ps1xml i zrobić kilka poprawek, aby kolorowanie było możliwe.

Mam osoba plik .format.ps1xml formatowanie na github.com: https://github.com/ecsousa/PSUtils/blob/master/CustomPSUtils.format.ps1xml

Aby go użyć, wszystko co musisz zrobić, to:

Update-FormatData -Prepend CustomPSUtils.format.ps1xml 

Ponadto, aby upewnić się, że wrócisz do oryginalnego koloru konsoli po Get-ChildItem, musisz zastąpić funkcję prompt. Coś takiego:

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

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

Właśnie zainstalowane i używane https://github.com/Davlind/PSColor który był bezbolesny. Obsługuje PSGet, dzięki czemu można łatwo zainstalować z Install-Module PSColor, aby go uzyskać.

Obiekty nie są przekształcane, więc nadal obsługują orurowanie. (Używa wspomnianego powyżej New-CommandWrapper)

Obsługuje również inne rzeczy, takie jak string wyboru.

PowerShell Color

+2

To prawdopodobnie powinna być zaakceptowana odpowiedź. W przeciwieństwie do innych tutaj (nie próbowałem joon's) dla mnie to po prostu działa bez modyfikacji (otrzymuję wiele błędów składniowych dla wszystkich innych odpowiedzi) i nie strasznie spowalnia ls. – stijn

Powiązane problemy