Изменить: Решение в нижней части этой публикации.
Colorizing Get-Childitem (иначе говоря, это не новая идея), но я не смог найти идеальные подходы к раскрашиванию вывода в Powershell. Существует два общих подхода для написания функций color-ls:
-
Перехват вывода Get-Childitem и повторное вывод его в виде текста с помощью Write-Host с параметром -ForegroundColor. Этот подход позволяет как можно больше детализации, но уменьшает вывод Get-Childitem в текст. Как известно большинству пользователей PowerShell, Get-Childitem не выводит текст, а выводит объекты. В частности, список объектов FileInfo и DirectoryInfo. Это позволяет обеспечить большую гибкость при обработке вывода Get-Childitem.
-
Выполните вывод Get-Childitem с помощью Invoke-Expression to Foreach-Object, изменив цвет переднего плана консоли перед выводом каждого объекта. Вид глотки, но лучший вариант, поскольку он сохраняет тип вывода Get-Childitem.
Вот пример последнего подхода, предоставленного Tim Johnson 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 $_
}
}
}
Этот код присваивает разные цвета, основанные исключительно на расширении файла, но почти любая метрика может быть заменена для различения типов файлов. Вышеприведенный код выводит следующий результат:
Это почти идеально, но есть один небольшой недостаток: первые 3 строки вывода (путь к каталогу, заголовки столбцов и горизонтальные разделители) принимают цвет первого элемента в списке. Тим Джонсон прокомментировал в своем блоге:
Я бы предпочел, чтобы заголовок сверху не всегда был того же цвета, что и первый элемент, но я не могу думать об этом.
Я тоже, к сожалению, не могу. Что там, где Qaru и его гуру powershell входят: я ищу способ раскрасить вывод Get-Childitem, сохраняя тип вывода командлета, не испортив цвет заголовка. Я сделал несколько экспериментов и возился с этим подходом, но пока не добился успеха, поскольку первый одиночный эхо-вызов выводит весь заголовок и первый элемент.
Любые вопросы, комментарии или даже лучшие решения приветствуются.
Решение Благодаря jon Z и другим, кто предоставил идеи:
Jon Z обеспечил идеальное решение этой проблемы, которое я немного отполировал, чтобы соответствовать схеме в моем первоначальном вопросе. Вот он, для всех, кто заинтересован. Обратите внимание, что для этого требуется командлет New-CommandWrapper из Поваренной книги Powershell. Весь этот код входит в ваш профиль.
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 ""
}
Это производит вывод, который выглядит следующим образом:
Если вам нужна общая строка размера файла внизу, просто добавьте следующий код:
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")
}
}