大家好,我是 IT大厨!
眼下的疫情不容小觑。前几天西安的小伙伴开启了核酸检测模式,昨天就轮到生活所在的片区全员检测。所以还是老实呆在家陪家人和学习吧。不管是不是程序员,学习才是王道。
工作原因写过不少脚本,所以把之前对PowerShell中(其它Shell也是类似)输出各种颜色的文字的方法加以总结,并形成成果供大家参考。有不尽之处还望指出,共同成长。
目录
缘起
对Shell的向往
本人工作以来浸淫Windows多年,虽偶有接触Linux但是大多都是用来学习些东西,加上它炫酷的Shell界面(装X神器)。所以一直对Linux的Shell界面有种莫名的向往。最留恋的莫过于下面的开机画面了。
启动过程中的各种检查日志输出,加上那不一样的绿色状态。就一直觉得很帅。
所以在自己工作中凡事遇到Shell/CMD的地方也总想像它一样在黑黑的屏幕上来点色彩,就像星空中的点点繁星。
Windows下的PowerShell
平时PowerShell用的最多,那么PowerShell是否也有类似的功能呢?其实是有的。
比如: Write-Host这个命令本身就可以设置输出的文字字体颜色和背景颜色。
Write-Host [[-Object] <Object>] [-NoNewline] #不换行 [-Separator <Object>] #分隔符 当输入多个字符串的时候可以使用这个选项 [-ForegroundColor <ConsoleColor>] #字体颜色 [-BackgroundColor <ConsoleColor>] #字体背景色 [<CommonParameters>]
那用这个命令能实现Linux下面的效果吗?
答案是可以的。
使用Write-Host 改变输出颜色
在PowerShell中要实现这样的功能可以用下面的几行代码来实现。
$message = "Checking CPU..." #要输出的消息
Write-Host $message.PadRight(50), "[ " -NoNewline #输出前半截,不换行
Write-Host "OK" -ForegroundColor:Green -NoNewline #输出状态,并设置格式
Write-Host " ]" #输出最后的括号并换行
效果如下:
当然如果每次要输出的时候都这么输肯定会很痛苦,而且要修改输出格式的时候是很不方便的。
那怎么办呢?可以考虑把上面的代码写成一个方法(function)这样每次需要输出类似的代码的时候就调用这个方法就可以了。
方法如下,当然这个里面加入了一些对参数的处理,核心的思想还是上面的三行代码。
function Format-Status {
param (
$Message,
$Status,
[System.ConsoleColor]
$ForegroundColor,
$BackgroundColor,
[int]
$Pading = 80
)
Write-Host $message.PadRight($Pading), "[ " -NoNewline
if ($ForegroundColor -and $BackgroundColor) {
Write-Host $Status -ForegroundColor:$ForegroundColor -NoNewline -BackgroundColor:$BackgroundColor
}
elseif ($ForegroundColor) {
Write-Host $Status -ForegroundColor:$ForegroundColor -NoNewline
}
elseif ($BackgroundColor) {
Write-Host $Status -NoNewline -BackgroundColor:$BackgroundColor
}
else {
Write-Host $Status -NoNewline
}
Write-Host " ]"
}
在控制台里面运行上面的方法之后就可以调用了:
现在,输出同样的内容只需要调一行代码了
Format-Status -Message $message -Status "OK" -ForegroundColor:Green
故事到这里似乎也就结束了。
非也非也
PowerShell不仅可以顺序执行命令,也可以异步执行。比如Start-Job和各种带-AsJob的命令都能异步执行。
Start-Job [-Name <String>] [-ScriptBlock] <ScriptBlock> [-Credential <PSCredential>] [-Authentication <AuthenticationMechanism>] [[-InitializationScript] <ScriptBlock>] [-WorkingDirectory <String>] [-RunAs32] [-PSVersion <Version>] [-InputObject <PSObject>] [-ArgumentList <Object[]>] [<CommonParameters>]
带-AsJob参数的命令:
Invoke-Command [[-Session] <PSSession[]>] [-ThrottleLimit <Int32>] [-AsJob] #输入这个参数会让该命令以job的方式执行,所以主线程里不会等待执行结束 [-HideComputerName] [-JobName <String>] [-FilePath] <String> [-RemoteDebug] [-InputObject <PSObject>] [-ArgumentList <Object[]>] [<CommonParameters>]
那这个又和刚才我们写好的方法有什么关系呢?
我们先对Format-Status稍加修改来模拟真实的执行环境:
function Format-Status {
param (
$Message,
$Status,
[System.ConsoleColor]
$ForegroundColor,
$BackgroundColor,
[int]
$Pading = 80
)
Write-Host $message.PadRight($Pading) -NoNewline
Start-Sleep -Milliseconds 100 #在脚本真正执行的时候有时候可能会有延迟
Write-Host "[ " -NoNewline
if ($ForegroundColor -and $BackgroundColor) {
Write-Host $Status -ForegroundColor:$ForegroundColor -NoNewline -BackgroundColor:$BackgroundColor
}
elseif ($ForegroundColor) {
Write-Host $Status -ForegroundColor:$ForegroundColor -NoNewline
}
elseif ($BackgroundColor) {
Write-Host $Status -NoNewline -BackgroundColor:$BackgroundColor
}
else {
Write-Host $Status -NoNewline
}
Write-Host " ]"
}
然后我们再起三个job来模拟真实环境下的多任务执行:
# 0..2 相当于生成一个 @(0,1,2)的数组,这个有点类似于JavaScript的ES6语法
$Jobs = (0..2 | % {
Start-Job -ScriptBlock {
#为了简单起见,把Format-Status放到Job里面
#因为每一个Job相当于我们的多线程里面的一个线程
#它有它自己执行的上下文
function Format-Status {
param (
$Message,
$Status,
[System.ConsoleColor]
$ForegroundColor,
$BackgroundColor,
[int]
$Pading = 80
)
Write-Host $message.PadRight($Pading) -NoNewline
Start-Sleep -Milliseconds 100
Write-Host "[ " -NoNewline
if ($ForegroundColor -and $BackgroundColor) {
Write-Host $Status -ForegroundColor:$ForegroundColor -NoNewline -BackgroundColor:$BackgroundColor
}
elseif ($ForegroundColor) {
Write-Host $Status -ForegroundColor:$ForegroundColor -NoNewline
}
elseif ($BackgroundColor) {
Write-Host $Status -NoNewline -BackgroundColor:$BackgroundColor
}
else {
Write-Host $Status -NoNewline
}
Write-Host " ]"
}
#Job里面循环21遍模拟实际执行工程中的内容
0..20 | % {
#耗时操作
Start-Sleep -Seconds ( Get-Random -Minimum 0 -Maximum 3)
$msg = @(
"CPU checking ... "
"Memory checking ..."
"Disk checking ..."
"Network checking ..."
"BIOS checking ..."
"Loading boot image ..."
)
$message = $msg[(Get-Random -Minimum 0 -Maximum ($msg.Count - 1))]
#执行结束输出结果
Format-Status -Message "[$_] $message" -Status "OK" -ForegroundColor:Green
}
}
})
$Jobs | Receive-Job -Wait -AutoRemoveJob #阻塞主线程,接收执行结果,自动销毁job
我们来看下模拟执行的效果:
一开始还OK,但是慢慢的出现了串行
为什么呢?因为我们的Format-Status里Write-Host使用了-NoNewline参数,而我们又在前后Write-Host之间设置了延迟,导致线程A刚刚输出不换行的内容之后,线程B又输出了自己的内容就直接接在了线程A输出的内容之后--导致串行。
我想说这并不是我故意这么设置导致输出这样的结果,而是在实际的开发/工作过程中遇到的真实案例。尤其是使用PowerShell执行远程命令并且是以job的形式执行的时候。
那既然如此还有没有更愉快的方法,让PowerShell输出的内容既炫又不会有刚才那样的问题?
答案坑定还是有的,要不我为啥要写这篇blog,O(∩_∩)O哈哈~
ANSI Color
好好的为啥提到ANSI Color?那到底什么又是ANSI Color?
不知道大家有没有注意到我们的PowerShell也好,CMD也好还是Linux也罢都能显示颜色,但是能显示的颜色都只有几种。拿PowerShell或者Windows下面的控制台程序为例:
Black
Blue
Green
Cyan
Red
Magenta
Yellow
White
当然还有个中间灰 Gray,以及与之对应的还有稍微暗一点的Dark系列
DarkBlue
DarkGreen
DarkGray
DarkCyan
DarkRed
DarkMagenta
DarkYellow
PowerShell中的默认颜色
但是无外乎都是这几种颜色。那么问题来了,谁规定的控制台就这几种颜色呢?在网上一顿研究之后最终目标指向了ANSI Color。
那什么是ANSI Color?
ANSI Color是由美国标准化组织ANSI制定的一系列安全色(ANSI Z535.1-2011),用于安全标识中。比如最常见的就是在消防安全中的各种颜色。终于知道原来各个场所中的各种标识的颜色并不是随便标的,而是有其标准的。下面是其每一种颜色以及其具体的意义:
哈哈,看了这个是不是对各种标识的颜色有个感性的认识了。计算机最早有显示器的时候也是几乎黑乎乎的一个屏幕,只有文字,能给文字加上颜色就很炫酷了。当然这些文字颜色应该也遵循这样的标准并延续至今(这只是个人猜测,没有考证)。还记得最早老师教各种DOS程序的时候输出的颜色就很有限,那个时候就觉得能输出几种不同的颜色就很激动了。
当然随着计算机软件和硬件的发展,这几种颜色已经远远不能适应大家的需求了。尤其是像Windows这样的图形计算机系统出现以后。但是控制台程序里面还是延续了这几种默认的颜色。不过控制台实际支持的颜色已经不止这几种了。
什么是SGR?
在探讨这个问题之前,大家先思考一下下面的问题。
如果有用过Linux下的vi或者vim工具的大牛应该知晓它们的强大。虽然他们只是款控制台的编辑工具,但是其提供的编辑功能是非常丰富和强大的,尤其是它还是在shell里面工作。这对作为服务器使用没有图形界面的Linux的来说无疑是非常实用的。
那么vi / vim如此强大的功能是如何实现的呢?
ANSI 转义序列(ANSI Escape Codes)
我们先来看下面这张表,这是从别的网站复制过来的。
name |
signature |
description |
|
A |
Cursor Up |
(n=1) |
Move cursor up by n |
B |
Cursor Down |
(n=1) |
Move |