Powershell: Coloreando correctamente la salida Get-Childitem de una vez por todas
colors (5)
Acabo de instalar y usé https://github.com/Davlind/PSColor que fue sencillo. Admite PSGet para que pueda instalarlo fácilmente con Install-Module PSColor
para obtenerlo.
Los objetos no se transforman, por lo que aún admiten tuberías. (Está usando el New-CommandWrapper
mencionado anteriormente)
También es compatible con otras cosas como select-string.
Editar: Solución al final de esta publicación.
Colorear Get-Childitem ( dir o ls , en otras palabras) no es una idea nueva exactamente, pero no he podido encontrar ningún enfoque ideal para colorear el resultado en Powershell. Hay dos enfoques generales para escribir funciones de color-ls:
Interceptar la salida de Get-Childitem y volver a enviarla como texto usando Write-Host con el parámetro -ForegroundColor. Este enfoque permite la mayor cantidad de granularidad posible, pero reduce la salida de Get-Childitem a texto. Como la mayoría de los usuarios de powershell saben, Get-Childitem no genera texto, sino que genera objetos. Específicamente, una lista de objetos FileInfo y DirectoryInfo. Esto permite una gran flexibilidad en el manejo de la salida Get-Childitem.
Transfiera la salida de Get-Childitem mediante Invoke-Expression a Foreach-Object, cambiando el color de primer plano de la consola antes de imprimir cada objeto. Clase de bocado, pero la mejor opción porque conserva el tipo de salida de Get-Childitem.
Aquí hay un ejemplo de este último enfoque, proporcionado por Powershell Blog de Tim Johnson .
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 $_
}
}
}
Este código asigna diferentes colores basados puramente en la extensión de archivo, pero casi cualquier métrica podría sustituirse para diferenciar los tipos de archivos. El código anterior produce el siguiente resultado:
Esto es casi perfecto, pero hay un pequeño defecto: el primer resultado de 3 líneas (ruta del directorio, encabezados de columna y separadores horizontales) toma el color del primer elemento en la lista. Tim Johnson comentó en su blog:
Preferiría que el encabezado en la parte superior no fuera siempre del mismo color que el primer elemento, pero no puedo pensar en ninguna manera de evitarlo.
Ni yo tampoco puedo, desafortunadamente. Ahí es donde entran Stack Overflow y sus gurús de powershell: estoy buscando una forma de colorear el resultado de Get-Childitem conservando el tipo de salida del cmdlet, sin estropear el color del encabezado. He hecho un poco de experimentación y violín con este enfoque, pero aún no he tenido éxito, ya que la primera llamada de eco único genera el encabezado completo y el primer elemento.
Cualquier pregunta, comentario o, mejor aún, soluciones son bienvenidas.
La solución Gracias a jon Z y a los demás que aportaron ideas:
Jon Z proporcionó la solución perfecta para este problema, que he pulido un poco para que coincida con el esquema en mi pregunta original. Aquí está, para cualquiera que esté interesado. Tenga en cuenta que esto requiere el cmdlet New-CommandWrapper del Powershell Cookbook. Todo este código va en su perfil.
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 ""
}
Esto produce un resultado que se parece a la siguiente captura de pantalla:
Si desea la línea de tamaño de archivo total en la parte inferior, simplemente agregue el siguiente código:
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")
}
}
La modificación de Out-Default definitivamente es el camino a seguir. A continuación un ejemplo concedido, descuidado. Estoy usando New-CommandWrapper del libro de cocina 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
}
}
Tal vez a través de una función proxy por defecto.
Tengo otra secuencia de comandos que se ocupa del caso Format-Wide
( ls
) y también tiene un mejor rendimiento al usar diccionarios en lugar de regex: https://github.com/joonro/Get-ChildItem-Color .
Tengo otra solución. Puede tener un archivo .format.ps1xml personalizado para él y hacer algunas modificaciones para que el color sea posible.
Tengo mi persona .format.ps1xml, archivo de formato en github.com: https://github.com/ecsousa/PSUtils/blob/master/CustomPSUtils.format.ps1xml
Para usarlo, todo lo que necesita hacer es:
Update-FormatData -Prepend CustomPSUtils.format.ps1xml
Además, para asegurarse de volver al color original de la consola después de Get-ChildItem, deberá anular prompt
función de prompt
. Algo como esto:
function prompt {
if($global:FSFormatDefaultColor) {
[Console]::ForegroundColor = $global:FSFormatDefaultColor
}
"PS $($executionContext.SessionState.Path.CurrentLocation)$(''>'' * ($nestedPromptLevel + 1)) "
}