Fop*_*ush 37 powershell colors
编辑:此帖子底部的解决方案.
着色Get-Childitem(换句话说,dir或ls)并不是一个新想法,但我无法在Powershell中找到任何理想的输出着色方法.编写color-ls函数有两种通用方法:
拦截Get-Childitem的输出,并使用带有-ForegroundColor参数的Write-Host将其重新输出为文本.此方法允许尽可能多的粒度,但将Get-Childitem的输出减少为文本.正如大多数PowerShell用户所知,Get-Childitem不输出文本,而是输出对象.具体来说,是FileInfo和DirectoryInfo对象的列表.这使得处理Get-Childitem输出具有很大的灵活性.
将Get-Childitem的输出通过Invoke-Expression传递给Foreach-Object,在输出每个对象之前更改控制台前景色.有点满口,但更好的选择,因为它保留了Get-Childitem的输出类型.
以下是Tim Johnson的Powershell博客提供的后一种方法的示例.
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 $_
}
}
}
Run Code Online (Sandbox Code Playgroud)
此代码仅基于文件扩展名分配不同的颜色,但几乎任何度量标准都可以替代以区分文件类型.上面的代码产生以下输出:

这几乎是完美的,但有一个小缺陷:前3行输出(目录路径,列标题和水平分隔符)采用列表中第一项的颜色.蒂姆约翰逊在他的博客中评论道:
我宁愿如果顶部的标题并不总是与第一个项目的颜色相同,但我想不出任何解决方法.
不幸的是,我也不能.这就是Stack Overflow及其powershell大师的用武之地:我正在寻找一种方法来着色Get-Childitem输出,同时保留cmdlet的输出类型,而不会弄乱标题的颜色.我已经做了一些实验和摆弄这种方法,但还没有取得任何成功,因为第一个单一的echo调用输出整个标题和第一项.
欢迎提出任何问题,意见或更好的解决方案.
解决方案感谢jon Z和其他提供想法的人:
Jon Z为这个问题提供了完美的解决方案,我在原始问题中对此方案进行了一些调整.对于任何有兴趣的人来说,这里都是.请注意,这需要Powershell Cookbook中的New-CommandWrapper cmdlet.相关的cmdlet已添加到此帖的底部.所有这些代码都在您的个人资料中.
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 ""
}
Run Code Online (Sandbox Code Playgroud)
这会产生类似于以下屏幕截图的输出:

如果您想在底部的总文件大小行,只需添加以下代码:
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")
}
}
Run Code Online (Sandbox Code Playgroud)
正如评论中指出的那样,PoshCode New-CommandWrapper链接已经死亡.以下是相关的cmdlet:
##############################################################################
##
## New-CommandWrapper
##
## From Windows PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Adds parameters and functionality to existing cmdlets and functions.
.EXAMPLE
New-CommandWrapper Get-Process `
-AddParameter @{
SortBy = {
$newPipeline = {
__ORIGINAL_COMMAND__ | Sort-Object -Property $SortBy
}
}
}
This example adds a 'SortBy' parameter to Get-Process. It accomplishes
this by adding a Sort-Object command to the pipeline.
.EXAMPLE
$parameterAttributes = @'
[Parameter(Mandatory = $true)]
[ValidateRange(50,75)]
[Int]
'@
New-CommandWrapper Clear-Host `
-AddParameter @{
@{
Name = 'MyMandatoryInt';
Attributes = $parameterAttributes
} = {
Write-Host $MyMandatoryInt
Read-Host "Press ENTER"
}
}
This example adds a new mandatory 'MyMandatoryInt' parameter to
Clear-Host. This parameter is also validated to fall within the range
of 50 to 75. It doesn't alter the pipeline, but does display some
information on the screen before processing the original pipeline.
#>
param(
## The name of the command to extend
[Parameter(Mandatory = $true)]
$Name,
## Script to invoke before the command begins
[ScriptBlock] $Begin,
## Script to invoke for each input element
[ScriptBlock] $Process,
## Script to invoke at the end of the command
[ScriptBlock] $End,
## Parameters to add, and their functionality.
##
## The Key of the hashtable can be either a simple parameter name,
## or a more advanced parameter description.
##
## If you want to add additional parameter validation (such as a
## parameter type,) then the key can itself be a hashtable with the keys
## 'Name' and 'Attributes'. 'Attributes' is the text you would use when
## defining this parameter as part of a function.
##
## The Value of each hashtable entry is a scriptblock to invoke
## when this parameter is selected. To customize the pipeline,
## assign a new scriptblock to the $newPipeline variable. Use the
## special text, __ORIGINAL_COMMAND__, to represent the original
## command. The $targetParameters variable represents a hashtable
## containing the parameters that will be passed to the original
## command.
[HashTable] $AddParameter
)
Set-StrictMode -Version Latest
## Store the target command we are wrapping, and its command type
$target = $Name
$commandType = "Cmdlet"
## If a function already exists with this name (perhaps it's already been
## wrapped,) rename the other function and chain to its new name.
if(Test-Path function:\$Name)
{
$target = "$Name" + "-" + [Guid]::NewGuid().ToString().Replace("-","")
Rename-Item function:\GLOBAL:$Name GLOBAL:$target
$commandType = "Function"
}
## The template we use for generating a command proxy
$proxy = @'
__CMDLET_BINDING_ATTRIBUTE__
param(
__PARAMETERS__
)
begin
{
try {
__CUSTOM_BEGIN__
## Access the REAL Foreach-Object command, so that command
## wrappers do not interfere with this script
$foreachObject = $executionContext.InvokeCommand.GetCmdlet(
"Microsoft.PowerShell.Core\Foreach-Object")
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand(
'__COMMAND_NAME__',
[System.Management.Automation.CommandTypes]::__COMMAND_TYPE__)
## TargetParameters represents the hashtable of parameters that
## we will pass along to the wrapped command
$targetParameters = @{}
$PSBoundParameters.GetEnumerator() |
& $foreachObject {
if($command.Parameters.ContainsKey($_.Key))
{
$targetParameters.Add($_.Key, $_.Value)
}
}
## finalPipeline represents the pipeline we wil ultimately run
$newPipeline = { & $wrappedCmd @targetParameters }
$finalPipeline = $newPipeline.ToString()
__CUSTOM_PARAMETER_PROCESSING__
$steppablePipeline = [ScriptBlock]::Create(
$finalPipeline).GetSteppablePipeline()
$steppablePipeline.Begin($PSCmdlet)
} catch {
throw
}
}
process
{
try {
__CUSTOM_PROCESS__
$steppablePipeline.Process($_)
} catch {
throw
}
}
end
{
try {
__CUSTOM_END__
$steppablePipeline.End()
} catch {
throw
}
}
dynamicparam
{
## Access the REAL Get-Command, Foreach-Object, and Where-Object
## commands, so that command wrappers do not interfere with this script
$getCommand = $executionContext.InvokeCommand.GetCmdlet(
"Microsoft.PowerShell.Core\Get-Command")
$foreachObject = $executionContext.InvokeCommand.GetCmdlet(
"Microsoft.PowerShell.Core\Foreach-Object")
$whereObject = $executionContext.InvokeCommand.GetCmdlet(
"Microsoft.PowerShell.Core\Where-Object")
## Find the parameters of the original command, and remove everything
## else from the bound parameter list so we hide parameters the wrapped
## command does not recognize.
$command = & $getCommand __COMMAND_NAME__ -Type __COMMAND_TYPE__
$targetParameters = @{}
$PSBoundParameters.GetEnumerator() |
& $foreachObject {
if($command.Parameters.ContainsKey($_.Key))
{
$targetParameters.Add($_.Key, $_.Value)
}
}
## Get the argumment list as it would be passed to the target command
$argList = @($targetParameters.GetEnumerator() |
Foreach-Object { "-$($_.Key)"; $_.Value })
## Get the dynamic parameters of the wrapped command, based on the
## arguments to this command
$command = $null
try
{
$command = & $getCommand __COMMAND_NAME__ -Type __COMMAND_TYPE__ `
-ArgumentList $argList
}
catch
{
}
$dynamicParams = @($command.Parameters.GetEnumerator() |
& $whereObject { $_.Value.IsDynamic })
## For each of the dynamic parameters, add them to the dynamic
## parameters that we return.
if ($dynamicParams.Length -gt 0)
{
$paramDictionary = `
New-Object Management.Automation.RuntimeDefinedParameterDictionary
foreach ($param in $dynamicParams)
{
$param = $param.Value
$arguments = $param.Name, $param.ParameterType, $param.Attributes
$newParameter = `
New-Object Management.Automation.RuntimeDefinedParameter `
$arguments
$paramDictionary.Add($param.Name, $newParameter)
}
return $paramDictionary
}
}
<#
.ForwardHelpTargetName __COMMAND_NAME__
.ForwardHelpCategory __COMMAND_TYPE__
#>
'@
## Get the information about the original command
$originalCommand = Get-Command $target
$metaData = New-Object System.Management.Automation.CommandMetaData `
$originalCommand
$proxyCommandType = [System.Management.Automation.ProxyCommand]
## Generate the cmdlet binding attribute, and replace information
## about the target
$proxy = $proxy.Replace("__CMDLET_BINDING_ATTRIBUTE__",
$proxyCommandType::GetCmdletBindingAttribute($metaData))
$proxy = $proxy.Replace("__COMMAND_NAME__", $target)
$proxy = $proxy.Replace("__COMMAND_TYPE__", $commandType)
## Stores new text we'll be putting in the param() block
$newParamBlockCode = ""
## Stores new text we'll be putting in the begin block
## (mostly due to parameter processing)
$beginAdditions = ""
## If the user wants to add a parameter
$currentParameter = $originalCommand.Parameters.Count
if($AddParameter)
{
foreach($parameter in $AddParameter.Keys)
{
## Get the code associated with this parameter
$parameterCode = $AddParameter[$parameter]
## If it's an advanced parameter declaration, the hashtable
## holds the validation and / or type restrictions
if($parameter -is [Hashtable])
{
## Add their attributes and other information to
## the variable holding the parameter block additions
if($currentParameter -gt 0)
{
$newParamBlockCode += ","
}
$newParamBlockCode += "`n`n " +
$parameter.Attributes + "`n" +
' $' + $parameter.Name
$parameter = $parameter.Name
}
else
{
## If this is a simple parameter name, add it to the list of
## parameters. The proxy generation APIs will take care of
## adding it to the param() block.
$newParameter =
New-Object System.Management.Automation.ParameterMetadata `
$parameter
$metaData.Parameters.Add($parameter, $newParameter)
}
$parameterCode = $parameterCode.ToString()
## Create the template code that invokes their parameter code if
## the parameter is selected.
$templateCode = @"
if(`$PSBoundParameters['$parameter'])
{
$parameterCode
## Replace the __ORIGINAL_COMMAND__ tag with the code
## that represents the original command
`$alteredPipeline = `$newPipeline.ToString()
`$finalPipeline = `$alteredPipeline.Replace(
'__ORIGINAL_COMMAND__', `$finalPipeline)
}
"@
## Add the template code to the list of changes we're making
## to the begin() section.
$beginAdditions += $templateCode
$currentParameter++
}
}
## Generate the param() block
$parameters = $proxyCommandType::GetParamBlock($metaData)
if($newParamBlockCode) { $parameters += $newParamBlockCode }
$proxy = $proxy.Replace('__PARAMETERS__', $parameters)
## Update the begin, process, and end sections
$proxy = $proxy.Replace('__CUSTOM_BEGIN__', $Begin)
$proxy = $proxy.Replace('__CUSTOM_PARAMETER_PROCESSING__', $beginAdditions)
$proxy = $proxy.Replace('__CUSTOM_PROCESS__', $Process)
$proxy = $proxy.Replace('__CUSTOM_END__', $End)
## Save the function wrapper
Write-Verbose $proxy
Set-Content function:\GLOBAL:$NAME $proxy
## If we were wrapping a cmdlet, hide it so that it doesn't conflict with
## Get-Help and Get-Command
if($commandType -eq "Cmdlet")
{
$originalCommand.Visibility = "Private"
}
Run Code Online (Sandbox Code Playgroud)
Thr*_*aka 18
我刚安装并使用https://github.com/Davlind/PSColor,这是无痛的.它支持PSGet,因此您可以轻松安装Install-Module PSColor以获取它.
物体没有变形,所以它们仍然支持管道.(它使用New-CommandWrapper上面提到的)
它还支持select-string等其他内容.

jon*_*n Z 13
修改Out-Default肯定是要走的路.在一个 - 授予,邋 - 的例子之下.我正在使用PowerShell Cookbook中的New-CommandWrapper.
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
}
}
Run Code Online (Sandbox Code Playgroud)

| 归档时间: |
|
| 查看次数: |
12226 次 |
| 最近记录: |