坑边闲话:忆往昔岁月,不堪回首,伟大的 Windows 竟然拿不出一个像样的终端模拟器。mintty.exe 和封装后的 cmder 之流,总是有各种问题,而且不兼容 emoji 字符。后来,全网 Windows 用户随着一个华丽的广告沸腾了,微软宣布了终端软件 Windows Terminal 的开发进程,而且开源!如今,Windows Terminal 正式版已经陪伴我们走过了很长一段时间,其稳定性和易用性已经非常不错,关键是颜值相当高。如果你是一个追求完美与和谐的 User,那么请跟上我的步伐,我们重新起航!

重要提醒:本文的所有配置经过无数网友的多重考验,请勿在配置过程中突发奇想走弯路,一定要认真阅读每一个段落、每一个句子!

图 1. 常规展示。

图 2. 在 git 目录下的效果。

1. 相关软件下载与安装·

为保证后续配置过程顺利,用户需要将所需要的软件、字体下载到本地。

1.1 Windows Terminal·

相信这一步对大多数人不构成任何困难,Microsoft Store 里搜索 App 名称并下载即可。

如果你下载的版本低于 v1.18,则需要到 GitHub 仓库下载最新的 Release 版本,注意选择 .msixbundle 格式。

点此下载 v1.18.3181.0

1.2 字体·

这里仅推荐一款字体:Fira Code Nerd Font. 该字体支持 ligature 连字功能,而且是一款专门为代码显示准备的字体,同时支持很多有趣的特殊字符,因此非常适合在终端里使用。该字体开源,广受海内外程序员好评!

点此下载 v3.0.2

装上该字体,即可进入下一步。

1.3 新版 PowerShell·

首先声明,此处使用的 PowerShell 与 Windows 自带的 PowerShell 并非同一个软件,除了功能相似和名字相同,两者内在天差地别。现阶段 Windows 10/11/Server 自带的 Powershell 错误提示较为冗长颜值很低速度慢,笔者认为不太值得从头开始学习。此处提及的新版 PowerShell 曾用名 PowerShell Core,属于 .Net Core 跨平台战略的一个重要组成部分,

在这个 GitHub 链接里,有目前 PowerShell 的最新版,建议新手从 release 中选最新正式版下载并安装。

点此下载 v7.4.2

2. 配置环境·

2.1 安装 PowerShell 插件·

以管理员身份打开刚装好的新版 PowerShell,逐行输入命令以安装插件。

1
2
3
4
5
6
7
8
# 1. 安装 PSReadline 包,该插件可以让命令行很好用,类似 zsh
Install-Module -Name PSReadLine -Scope CurrentUser

# 2. 安装 posh-git 包,让你的 git 更好用
Install-Module posh-git -Scope CurrentUser

# 3. 安装 oh-my-posh 包,让你的命令行更酷炫、优雅
winget install JanDeDobbeleer.OhMyPosh -s winget

安装过程可能有点慢,好像卡住了一样,但是请耐心等待几分钟。等不及的同学自行搜索科学方法访问 GitHub. 安装时系统会提问是否继续,可直接输入 A 并回车表示同意。

在上述插件安装的第三步,需要使用 win10 自带的 winget 命令行工具。如果提示找不到命令,可以到 Microsoft Store 搜索应用安装程序,安装完成即可使用 winget.

2.2 配置 Windows Terminal·

新款 PowerShell 与 Windows Terminal 才是最佳搭配,理由如下:

  • PowerShell 提供了一个强大的解释器以及与 NT 内核交互的语法;
  • Windows Terminal 作为一个优秀的现代终端,能以极高的质量和飞快的速度渲染 PowerShell 文本,带给用户良好的体验。

打开 Windows Terminal 配置界面,打开 json 配置文件(ctrl+, 或者在设置界面点击左下角打开 JSON 文件按钮),按需修改即可。下面给出代码提示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
    "alwaysShowTabs": true,
"confirmCloseAllTabs": false,
"copyFormatting": "none",
"focusFollowMouse": false,
"profiles":
{
"defaults":
{
"colorScheme": "Homebrew",
"cursorShape": "filledBox",
"font":
{
"face": "FiraCode Nerd Font"
},
"useAtlasEngine": true
},
"list":
[
{
"closeOnExit": "graceful",
"colorScheme": "Homebrew",
"commandline": "C:/Program Files/PowerShell/7/pwsh.exe -nologo",
"cursorColor": "#FFFFFF",
"cursorShape": "emptyBox",
"font":
{
"face": "FiraCode Nerd Font",
"size": 11.0
},
"guid": "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
"hidden": false,
"historySize": 9001,
"icon": "C:/Program Files/PowerShell/7/assets/Powershell_av_colors.ico",
"name": "PowerShell 7",
"padding": "8",
"scrollbarState": "visible",
"snapOnInput": true,
"source": "Windows.Terminal.PowershellCore",
"startingDirectory": ".",
"tabTitle": "PowerShell"
}
]
},
},

注意

font.face 一定要填写正确,FiraCode 还有一个 Mono 变体也可以提供类似的效果,但是实测在 Windows Terminal 平台会出现 icon 类字符显示尺寸较小的问题。

同时附上 Homebrew 配色,该配色经过我改良。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"name": "Homebrew",
"black": "#000000",
"red": "#FC5275",
"green": "#00a600",
"yellow": "#999900",
"blue": "#6666e9",
"purple": "#b200b2",
"cyan": "#00a6b2",
"white": "#bfbfbf",
"brightBlack": "#666666",
"brightRed": "#e50000",
"brightGreen": "#00d900",
"brightYellow": "#e5e500",
"brightBlue": "#0000ff",
"brightPurple": "#e500e5",
"brightCyan": "#00e5e5",
"brightWhite": "#e5e5e5",
"background": "#283033",
"foreground": "#00ff00"
},

特别注意,用其他配色可能降低颜值

为了保证不同背景色下文本显示的兼容性,建议用户开启 Windows Terminal 的 AtlasEngine 文本渲染引擎。

图 3. 开启 Atlas Engine

同时,启用 AtlasEngine 之后,最大化的 Windows Terminal 窗口能显示的文本行数有轻微变化,一般不影响使用。

2.3 添加 Windows 右键菜单·

为方便用户在桌面环境中打开 Windows Terminal,建议在注册表中添加相关表项。

警告

这里涉及修改注册表,小白请勿手残改坏注册表,强烈建议事前建立系统还原点!

Github 上面已经有国外开发者写了 PowerShell 脚本,用户使用管理员身份运行该脚本搭配某些参数即可实现右键菜单的个性化配置。

点击打开原版 GitHub 仓库 点击打开我修改后的脚本仓库

建议下载后者,然后在管理员模式的 pwsh.exe 里执行下述命令(先不要在 Windows Terminal 里执行!):

1
2
3
4
5
# pwsh.exe, Administrator 模式
cd $env:USERPROFILE\Desktop
git clone [email protected]:LittleNewton/wt-here.git
cd .\wt-here
.\install.ps1 mini

再次提醒:记住一定要以管理员身份在 pwsh.exe 里面运行该脚本。

图 4. 用我修改后的脚本安装之后的效果如图。

2.4 添加 PowerShell 启动参数·

在 PowerShell 中输入下列命令编辑其启动配置。

1
notepad $PROFILE

当然,你也可以把上述命令中的 notepad (记事本)换成 code (VSCode) 进行编辑。

紧接着在弹出的页面中输入下面这一长串代码,保存并关闭。这个 Profile 配置文件与 .zshrc / .bashrc 文件一样,都是控制启动前参数的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
<#
* FileName: Microsoft.PowerShell_profile.ps1 / PowerShell configuration file.
* Author: 刘 鹏
* Email: [email protected]
* Created Date: 2021, Aug. 21
* Updated Date: 2023, Oct. 11
* Copyright: No copyright. You can use this code for anything with no warranty.
#>





#------------------------------- Import Modules BEGIN -------------------------------
# 引入 posh-git
Import-Module posh-git

# 引入 ps-read-line
Import-Module PSReadLine

# 设置 PowerShell 主题
oh-my-posh init pwsh --config $env:POSH_THEMES_PATH\paradox.omp.json | Invoke-Expression

# if Windows server 2022
# oh-my-posh init pwsh --config C:\Program Files (x86)\oh-my-posh\themes\paradox.omp.json | Invoke-Expression
#------------------------------- Import Modules END -------------------------------





#------------------------------- Set Hot-keys BEGIN -------------------------------
# 设置预测文本来源为历史记录
Set-PSReadLineOption -PredictionSource History

# 每次回溯输入历史,光标定位于输入内容末尾
Set-PSReadLineOption -HistorySearchCursorMovesToEnd

# 设置向上键为后向搜索历史记录
Set-PSReadLineKeyHandler -Key UpArrow -Function HistorySearchBackward

# 设置向下键为前向搜索历史纪录
Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward

# 设置 Tab 为菜单补全和 Intellisense
Set-PSReadLineKeyHandler -Key "Tab" -Function MenuComplete

# 回到行首/行尾
Set-PSReadlineKeyHandler -Key "Ctrl+a" -Function BeginningOfLine
Set-PSReadlineKeyHandler -Key "Ctrl+e" -Function EndOfLine

# 前进/后退一个单词
Set-PSReadlineKeyHandler -Key 'Alt+f' -Function ShellForwardWord
Set-PSReadlineKeyHandler -Key 'Alt+b' -Function ShellBackwardWord

# 设置 Ctrl D 为退出
Set-PSReadlineKeyHandler -Key "Ctrl+d" -Function ViExit

# 设置 Ctrl Z 为撤销
Set-PSReadLineKeyHandler -Key "Ctrl+z" -Function Undo

# 启用预测性 IntelliSense
Set-PSReadLineOption -PredictionSource History
#------------------------------- Set Hot-keys END -------------------------------





#------------------------------- Functions BEGIN -------------------------------
# Python 直接执行
$env:PATHEXT += ";.py"

# 更新系统组件
function Update-Packages {

# (1) Update Conda
# Reference: https://docs.conda.io/projects/conda/en/latest/commands/update.html
Write-Host "Step 1: 更新 Anaconda base 虚拟环境" -ForegroundColor Magenta -BackgroundColor Cyan
conda update -n base --yes --all
if ($LASTEXITCODE -ne 0) {
Write-Error -Message "Failed to upgrade Conda base environment."
return
}

# (2) Update TeXLive
$CurrentYear = Get-Date -Format yyyy
Write-Host "Step 2: 更新 TeX Live" $CurrentYear -ForegroundColor Magenta -BackgroundColor Cyan
$commandName = "tlmgr"
if (Get-Command $commandName -ErrorAction SilentlyContinue) {
tlmgr update --self --all
} else {
Write-Warning "The command '$commandName' does not exist."
Write-Warning "Now continue."
}
if ($LASTEXITCODE -ne 0) {
Write-Error -Message "Failed to upgrade TeXLive packages."
return
}

# (3) Update Apps using winget
Write-Host "Step 3: 通过 winget 更新 Windows 应用程序" -ForegroundColor Magenta -BackgroundColor Cyan
winget upgrade
if ($LASTEXITCODE -ne 0) {
Write-Error -Message "Failed to upgrade winget."
return
}
# winget upgrade --all
}
#------------------------------- Functions END -------------------------------





#------------------------------- Set Alias BEGIN -------------------------------
# 1. 编译函数 make
function MakeThings {
nmake.exe $args -nologo
}
Set-Alias -Name make -Value MakeThings

# 2. 更新系统 os-update
Set-Alias -Name os-update -Value Update-Packages

# 3. 查看目录 ls & ll
function ListDirectory {
(Get-ChildItem).Name
Write-Host("")
}
Set-Alias -Name ls -Value ListDirectory
Set-Alias -Name ll -Value Get-ChildItem

# 4. 打开当前工作目录
function Open-CurrentFolder {
[CmdletBinding()]
[Alias("open")]
param
(
[Parameter(Mandatory=$false)]
[string]
# 输入要打开的路径
# 用法示例:open C:\
# 默认路径:当前工作文件夹
$Path = (Get-Location)
)

# Resolve the path to a full path (absolute path)
$FullPath = Resolve-Path -Path $Path

# Check if the path exists and it's a directory
if ((Test-Path -Path $FullPath) -and ((Get-Item -Path $FullPath) -is [System.IO.DirectoryInfo])) {
Invoke-Item -Path $FullPath
} else {
Write-Error -Message "The path $Path does not exist or is not a directory."
}
}

# 5. 更改工作目录
function Update-Directory {
param (
# 输入要切换到的路径
# 用法示例:cd D:/
# 默认路径:C 盘的桌面
$Path = $env:USERPROFILE + '\Desktop'
)
Set-Location $Path
}
Set-Alias -Name cd -Value Update-Directory -Option AllScope
#------------------------------- Set Alias END -------------------------------





#------------------------------- Set Network BEGIN -------------------------------
# 1. 获取所有 Network Interface
function Get-AllNic {
Get-NetAdapter | Sort-Object -Property MacAddress
}
Set-Alias -Name getnic -Value Get-AllNic

# 2. 获取 IPv4 关键路由
function Get-IPv4Routes {
Get-NetRoute -AddressFamily IPv4 | Where-Object -FilterScript { $_.NextHop -ne '0.0.0.0' }
}
Set-Alias -Name getip -Value Get-IPv4Routes

# 3. 获取 IPv6 关键路由
function Get-IPv6Routes {
Get-NetRoute -AddressFamily IPv6 | Where-Object -FilterScript { $_.NextHop -ne '::' }
}
Set-Alias -Name getip6 -Value Get-IPv6Routes
#------------------------------- Set Network END -------------------------------





#------------------------------- Set ENV_VAR BEGIN -------------------------------
function Set-NasEnvironmentVariable {

# Documents
Set-Item -Path Env:\doc -Value 'W:'

# Downloads
Set-Item -Path Env:\download -Value "X:"
Set-Item -Path Env:\xunlei -Value $env:download"\Xunlei_Downloads"
Set-Item -Path Env:\byr -Value $env:download"\BYR_Download"

# Media
Set-Item -Path Env:\media -Value "Y:"
Set-Item -Path Env:\jellyfin -Value $env:media"\db_jellyfin"
Set-Item -Path Env:\failed -Value $env:mdcx"\failed"

# Software
Set-Item -Path Env:\soft -Value 'Z:'
}
Set-NasEnvironmentVariable
#------------------------------- Set ENV_VAR END -------------------------------





#------------------------------- Linux Control BEGIN -------------------------------
function Update-RemoteServer {
$debianServers = [ordered]@{
"5820-debian" = @{
Host = "10.1.2.21"
Port = "21221"
}
"epyc-debian" = @{
Host = "10.2.1.21"
Port = "22121"
}
"z690-debian" = @{
Host = "10.2.3.21"
Port = "22321"
}
"r740-debian" = @{
Host = "10.2.4.21"
Port = "22421"
}
"t630-debian" = @{
Host = "10.2.5.21"
Port = "22521"
}
"nipc-debian" = @{
Host = "10.10.1.21"
Port = "10121"
}
"7060-debian" = @{
Host = "10.3.1.21"
Port = "23121"
}
"x1e-debian" = @{
Host = "10.3.2.21"
Port = "23221"
}
"x299-debian" = @{
Host = "10.3.3.21"
Port = "23321"
}
}

$localUsername = "newton"

foreach ($server in $debianServers.GetEnumerator()) {
$serverName = $server.Key
$serverInfo = $server.Value
$serverHost = $serverInfo.Host
$serverPort = $serverInfo.Port

# Upgrade
Write-Host "===================================== ↓ ↓ ↓ =====================================" -ForegroundColor Magenta
Write-Host "$serverName is getting updated" -ForegroundColor Magenta
Start-Process -NoNewWindow -Wait -FilePath "ssh" -ArgumentList "$localUsername@$serverHost", "-p", $serverPort, "source ~/.config/zsh/.zshrc && os-update"
if ($LASTEXITCODE -ne 0) {
Write-Error -Message "Failed to upgrade $serverName"
} else {
Write-Host "$serverName upgrading done" -ForegroundColor Magenta
Write-Host ""
}
}
Write-Host "All upgrading jobs are done." -ForegroundColor Magenta
Write-Host ""
}
Set-Alias -Name remote-update -Value Update-RemoteServer
#------------------------------- Linux Control END -------------------------------

# Clear-Host





Set-PSReadLineKeyHandler -Chord '"',"'" `
-BriefDescription SmartInsertQuote `
-LongDescription "Insert paired quotes if not already on a quote" `
-ScriptBlock {
param($key, $arg)

$line = $null
$cursor = $null
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)

if ($line.Length -gt $cursor -and $line[$cursor] -eq $key.KeyChar) {
# Just move the cursor
[Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1)
}
else {
# Insert matching quotes, move cursor to be in between the quotes
[Microsoft.PowerShell.PSConsoleReadLine]::Insert("$($key.KeyChar)" * 2)
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
[Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor - 1)
}
}




Set-PSReadLineKeyHandler -Chord Ctrl+k `
-BriefDescription ClearHost `
-LongDescription "Clear the console host screen" `
-ScriptBlock {
Clear-Host
ClearScreen
}

3. 视频教程·

由于时间问题,视频不可能随时编辑,因此可能存在过期。一切以博客为准。

结束语·

经过上述配置,基本上可以在 Windows 平台实现良好的命令行体验。最关键的是 PowerShell 的语法设计非常出色,cmdlet 编程也非常具有美感。

最后回答几个问题。为什么不用 WSL 作为默认界面?当然,对于低性能电脑来说这也很好。但是我认为使用 VMware Workstation Pro 起一个 debian 12 虚拟机,然后在虚拟机及 VM network 里使用 Linux 是个更好的选择,因为这样无需开启 Hyper-V,宿主机性能不会损失太多。当然最重要的是 PowerShell 本身的可编程能力非常出众,多熟悉一门优秀的脚本语言也是强化技能树的重要方式。

局限性:PowerShell 启动较慢,加上本文设置的插件、前置命令,启动速度更慢。比起 Linux oh-my-zsh 要慢一个数量级,总体时间在 3 秒左右。因此,不建议频繁开启、关闭 Windows Terminal.