PowerShell 技能连载 - 提示符与界面定制

适用于 PowerShell 7.0 及以上版本

每天在终端里敲命令数小时,默认的 PS C:\> 提示符只能告诉你当前路径,其他信息一概欠奉。当你在多个 Git 仓库之间切换、管理不同的 Azure 订阅、激活不同的 Python 虚拟环境时,一个信息丰富的提示符可以让你瞬间掌握上下文状态,减少低级错误。

PowerShell 的提示符本质上就是一个名为 prompt 的函数——你可以自由重写它。无论是显示 Git 分支和脏状态、上一次命令的执行耗时、当前用户权限级别,还是用颜色区分不同的服务器环境,都可以通过几行代码实现。本文将带你从手写 prompt 函数开始,再到集成 Oh My Posh 这类成熟框架,最后补充一套提升日常效率的实用工具函数。

手写自定义 prompt 函数

最直接的方式是重写 prompt 函数。下面这段代码实现了一个多行提示符,第一行显示时间、路径和 Git 状态,第二行是实际的输入光标。同时它还记录上一条命令的执行时间,方便你判断某个操作是否太慢。

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
# 保存到 $PROFILE 中即可生效
# 记录命令开始时间
$global:__LastCommandStart = $null

# 在命令执行前记录时间
$ExecutionContext.SessionState.InvokeCommand.AddEventHandler(
'CommandSearchAction', {
$global:__LastCommandStart = [DateTime]::Now
}
)

# 获取 Git 分支与状态信息
function Get-GitStatus {
try {
$branch = git rev-parse --abbrev-ref HEAD 2>$null
if (-not $branch) { return '' }

$status = git status --porcelain 2>$null
$dirty = if ($status) { '*' } else { '' }

$ahead = git log "@{upstream}..HEAD" --oneline 2>$null
$aheadCount = ($ahead | Where-Object { $_ }).Count

$aheadMark = if ($aheadCount -gt 0) { "+$aheadCount" } else { '' }

return " [$branch$dirty$aheadMark]"
} catch {
return ''
}
}

# 获取上一次命令执行耗时
function Get-LastCommandDuration {
if (-not $global:__LastCommandStart) { return '' }
$duration = [DateTime]::Now - $global:__LastCommandStart
if ($duration.TotalSeconds -gt 1) {
return " ($([math]::Round($duration.TotalSeconds, 1))s)"
}
return ''
}

# 自定义 prompt 函数
function prompt {
$path = Get-Location
$homePrefix = $HOME -replace '\\', '\\'
$displayPath = $path.Path -replace "^$homePrefix", '~'
$gitInfo = Get-GitStatus
$duration = Get-LastCommandDuration
$timeStamp = Get-Date -Format 'HH:mm:ss'

# 第一行:时间戳 + 路径 + Git 状态 + 执行耗时
Write-Host "`n" -NoNewline
Write-Host $timeStamp -ForegroundColor DarkGray -NoNewline
Write-Host " " -NoNewline
Write-Host $displayPath -ForegroundColor Cyan -NoNewline
Write-Host $gitInfo -ForegroundColor Yellow -NoNewline
Write-Host $duration -ForegroundColor DarkYellow -NoNewline

# 权限提示
if (
$IsWindows -and
([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
) {
Write-Host " [ADMIN]" -ForegroundColor Red -NoNewline
}

# 第二行:输入提示符
Write-Host ""
Write-Host ">" -ForegroundColor Green -NoNewline
return ' '
}

执行后的终端效果如下(纯文本模拟):

1
2
14:32:05 ~/projects/my-app [main*+2] (3.2s)
>

第一行显示了当前时间、相对主目录的路径、Git 分支名称(main)、脏标记(*表示有未提交的更改)、领先远程的提交数(+2)以及上一条命令耗时 3.2 秒。如果在管理员模式下运行,还会出现红色的 [ADMIN] 标记。

集成 Oh My Posh

手动写 prompt 函数虽然灵活,但维护成本不低——尤其是当你想要图标、颜色主题、多种 Segment(环境变量、云平台信息等)时。Oh My Posh 是一个跨 Shell 的提示符渲染引擎,配合 Nerd Font 可以实现非常精美的终端外观。

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
# 安装 Oh My Posh(Windows 推荐使用 winget)
# winget install JanDeDobbeleer.OhMyPosh -s winget

# macOS / Linux 使用 Homebrew
# brew install jandedobbeleer/oh-my-posh/oh-my-posh

# 在 $PROFILE 中初始化 Oh My Posh
oh-my-posh init pwsh --config "$env:POSH_THEMES_PATH\jandedobbeleer.omp.json" |
Invoke-Expression

# 如果想使用自定义配置文件
# $ompConfig = Join-Path $HOME '.config' 'oh-my-posh' 'my-theme.omp.json'
# oh-my-posh init pwsh --config $ompConfig | Invoke-Expression

# 查看所有内置主题
Get-ChildItem -Path $env:POSH_THEMES_PATH -Filter '*.omp.json' |
Select-Object -ExpandProperty Name |
Sort-Object

# 快速预览主题(逐个浏览)
function Show-PoshThemePreview {
param([int]$Index = 0)

$themes = Get-ChildItem -Path $env:POSH_THEMES_PATH -Filter '*.omp.json' |
Sort-Object Name
$theme = $themes[$Index]
Write-Host "Theme [$Index/$($themes.Count)]: $($theme.Name)" -ForegroundColor Cyan
oh-my-posh init pwsh --config $theme.FullName | Invoke-Expression
}

# 导出当前配置并按需修改
function Export-PoshConfig {
param(
[string]$OutputPath = (Join-Path $HOME '.config' 'oh-my-posh')
)

$null = New-Item -ItemType Directory -Path $OutputPath -Force
$defaultConfig = Join-Path $env:POSH_THEMES_PATH 'jandedobbeleer.omp.json'
Copy-Item $defaultConfig (Join-Path $OutputPath 'my-theme.omp.json') -Force
Write-Host "配置已导出到 $OutputPath\my-theme.omp.json" -ForegroundColor Green
Write-Host '修改后更新 $PROFILE 中的 init 命令指向新文件即可。'
}

执行 Get-ChildItem 查看主题列表的部分输出:

1
2
3
4
5
6
1_shell.omp.json
agnoster.omp.json
agnosterplus.omp.json
atomic.omp.json
atomicBit.omp.json
...(共 100+ 内置主题)

执行 Export-PoshConfig 的输出:

1
2
配置已导出到 C:\Users\victor\.config\oh-my-posh\my-theme.omp.json
修改后更新 $PROFILE 中的 init 命令指向新文件即可。

Oh My Posh 的 JSON 配置文件支持丰富的 Segment 类型——Git、Az(Azure)、Python、Node、Docker、Kubectl 等等,你可以按需启用或禁用,调整颜色和图标。推荐从默认主题复制一份然后逐步微调,而不是从零开始编写。

实用工具函数集

提示符之外,Profile 里还可以放一些高频使用的辅助函数,它们与提示符配合让日常操作更加流畅。下面这组函数涵盖了目录快速跳转、增强的命令历史搜索,以及别名管理。

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
# --- 目录快速跳转 ---
# 使用书签机制在常用目录间跳转
$global:DirectoryBookmarks = @{}

function Set-Bookmark {
param([string]$Name)
$global:DirectoryBookmarks[$Name] = (Get-Location).Path
Write-Host "书签 '$Name' 已保存: $($global:DirectoryBookmarks[$Name])" -ForegroundColor Green
}

function Enter-Bookmark {
param([string]$Name)
if ($global:DirectoryBookmarks.ContainsKey($Name)) {
Set-Location $global:DirectoryBookmarks[$Name]
} else {
Write-Warning "书签 '$Name' 不存在。已保存的书签:"
$global:DirectoryBookmarks.GetEnumerator() |
ForEach-Object { Write-Host " $($_.Key) => $($_.Value)" }
}
}

# 简写别名
Set-Alias -Name bm -Value Set-Bookmark
Set-Alias -Name gb -Value Enter-Bookmark

# --- 增强的历史搜索 ---
# 使用 fzf(可选)或 PSFzf 模块进行模糊搜索
# 这里提供一个不依赖外部工具的方案
function Search-History {
param([string]$Pattern = '*')

Get-Content (Get-PSReadlineOption).HistorySavePath |
Where-Object { $_ -like "*$Pattern*" } |
Select-Object -Unique -Last 20
}

Set-Alias -Name hh -Value Search-History

# --- 别名管理 ---
# 列出所有自定义别名及其来源
function Get-MyAliases {
$builtIn = Get-Alias |
Where-Object { $_.Options -notcontains 'UserDefined' } |
Select-Object -ExpandProperty Name

Get-Alias |
Where-Object { $_.Name -notin $builtIn } |
Select-Object Name, Definition, Source |
Sort-Object Name |
Format-Table -AutoSize
}

# 快速进入 Profile 编辑模式
function Edit-Profile {
param([switch]$OpenFolder)
if ($OpenFolder) {
Invoke-Item (Split-Path $PROFILE)
} else {
code $PROFILE
}
}

Set-Alias -Name ep -Value Edit-Profile

使用书签功能的交互示例:

1
2
3
4
5
6
7
8
9
10
PS ~/projects/my-app> bm work
书签 'work' 已保存: /Users/victor/projects/my-app

PS ~> gb work
PS /Users/victor/projects/my-app>

PS ~> hh git
git status
git log --oneline -10
git push origin main

书签机制不依赖外部工具,设置简单,适合在少数几个高频目录之间切换。如果你的目录结构比较复杂,也可以考虑搭配 zzoxide 这类基于频率的跳转工具使用。

注意事项

  1. prompt 函数必须返回字符串:即使你只用 Write-Host 输出内容,函数也必须 return 一个字符串(哪怕是一个空格或空字符串),否则 PowerShell 会使用默认的提示符。
  2. Git 状态检测有性能开销:在大型仓库中,git status --porcelain 可能较慢。如果感到提示符延迟,可以在 Get-GitStatus 中加一个超时判断,或改用 git diff --quiet 做轻量级检测。
  3. Oh My Posh 需要 Nerd Font:图标符号依赖 Nerd Font 字体。如果终端中看到方框或乱码,说明字体未正确安装。推荐使用 CaskaydiaCove Nerd FontFiraCode Nerd Font
  4. Profile 分模块管理:随着自定义内容增多,建议把 prompt、别名、函数拆到不同的 .ps1 文件中,在 $PROFILE 里用 . $path 点源加载,保持主文件简洁。
  5. 跨平台兼容性:本文代码同时适配 Windows、macOS 和 Linux,但管理员检测部分([Security.Principal.WindowsIdentity])只在 Windows 上生效,非 Windows 平台会自动跳过该逻辑。
  6. PSReadLine 是好搭档:提示符定制之外,Set-PSReadlineOption 可以配置预测文本来源、颜色主题和快捷键。结合 CommandPrediction 插件,终端体验可以接近 IDE 级别。

PowerShell 技能连载 - Windows Terminal 定制

适用于 PowerShell 7.0 及以上版本

Windows Terminal 已经成为 Windows 平台上最受欢迎的终端模拟器之一。它支持多标签页、GPU 加速渲染、Unicode 和 UTF-8 字符显示,以及对 WSL、CMD、PowerShell 等多种 Shell 的统一管理。但对于日常重度使用命令行的开发者来说,默认的 Terminal 外观和功能往往不够用——提示符单调、配色平庸、缺少上下文信息,这些都会影响工作效率。

好消息是,借助 PowerShell 7 的强大生态,我们可以通过 Oh My Posh 主题引擎、Profile 脚本自动化以及 Terminal 的 JSON 配置,打造一个既美观又实用的终端环境。从 Git 状态感知的提示符,到一键切换配色方案,再到自定义快捷键绑定,几乎所有的视觉和行为要素都可以按需调整。

本文将从实际场景出发,逐步展示如何用 PowerShell 脚本完成 Windows Terminal 的深度定制。每一段代码都可以直接复制到你的环境中运行,让你在几分钟内拥有一个令人印象深刻的终端工作区。

安装和初始化 Oh My Posh

Oh My Posh 是一个跨平台的提示符主题引擎,可以为 PowerShell 提供丰富的上下文信息,包括 Git 分支状态、Python 虚拟环境、执行耗时等。首先我们需要安装它并配置到 Profile 中。

1
2
3
4
5
6
7
8
# 安装 Oh My Posh(使用 winget)
winget install JanDeDobbeleer.OhMyPosh -s winget

# 如果 winget 不可用,也可以用 PowerShell 直接安装
Install-Module oh-my-posh -Scope CurrentUser -Force

# 查看 Oh My Posh 版本确认安装成功
oh-my-posh --version

安装完成后,输出类似如下:

1
24.5.0

接下来将 Oh My Posh 初始化命令写入 Profile,使其在每次启动 PowerShell 时自动加载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 查找或创建 Profile 文件
$profilePath = $PROFILE.CurrentUserAllHosts
if (-not (Test-Path $profilePath)) {
$null = New-Item -Path $profilePath -ItemType File -Force
Write-Host "已创建 Profile 文件: $profilePath"
} else {
Write-Host "Profile 文件已存在: $profilePath"
}

# 向 Profile 中追加 Oh My Posh 初始化行
$initLine = 'oh-my-posh init pwsh | Invoke-Expression'
$content = Get-Content -Path $profilePath -Raw -ErrorAction SilentlyContinue
if ($content -notmatch 'oh-my-posh init') {
Add-Content -Path $profilePath -Value $initLine
Write-Host '已添加 Oh My Posh 初始化命令'
} else {
Write-Host 'Oh My Posh 初始化命令已存在,跳过'
}

执行结果示例:

1
2
已创建 Profile 文件: C:\Users\dev\Documents\PowerShell\profile.ps1
已添加 Oh My Posh 初始化命令

浏览和应用主题

Oh My Posh 内置了大量开箱即用的主题,你可以通过脚本快速预览并切换。以下代码列出所有可用主题,并让你预览效果。

1
2
3
4
5
6
7
8
9
10
11
12
# 获取 Oh My Posh 主题目录
$themesDir = "$(oh-my-posh config export themes)"

# 列出所有主题文件名
$themes = Get-ChildItem -Path $themesDir -Filter '*.omp.json' |
Select-Object -ExpandProperty BaseName

Write-Host "共找到 $($themes.Count) 个主题"
Write-Host '---'
foreach ($t in $themes) {
Write-Host " - $t"
}

执行结果示例:

1
2
3
4
5
6
7
8
9
10
11
共找到 127 个主题
---
- 1_shell
- agnoster
- agnosterplus
- aliens
- amro
- atomic
- atomicBit
- avit
...

找到喜欢的主题后,将其写入 Profile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 选择主题名称
$selectedTheme = 'jandebuhr'

# 构建 Oh My Posh 初始化命令(指定主题)
$themeInit = "oh-my-posh init pwsh --config `"$themesDir\$selectedTheme.omp.json`" | Invoke-Expression"

# 读取当前 Profile 内容
$profileContent = Get-Content -Path $PROFILE.CurrentUserAllHosts -Raw

# 替换已有的 oh-my-posh init 行,或追加新行
if ($profileContent -match 'oh-my-posh init pwsh') {
$profileContent = $profileContent -replace 'oh-my-posh init pwsh.*Invoke-Expression', $themeInit
Set-Content -Path $PROFILE.CurrentUserAllHosts -Value $profileContent -NoNewline
Write-Host "已更新主题为: $selectedTheme"
} else {
Add-Content -Path $PROFILE.CurrentUserAllHosts -Value $themeInit
Write-Host "已添加主题: $selectedTheme"
}

Write-Host '请重新打开终端以查看效果'

执行结果示例:

1
2
已更新主题为: jandebuhr
请重新打开终端以查看效果

自动化管理 Terminal 配置文件

Windows Terminal 的设置存储在一个 JSON 文件中,路径通常是 settings.json。我们可以用 PowerShell 脚本直接读取和修改它,实现配色方案的批量管理和快捷键自定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 定位 Windows Terminal settings.json 路径
$settingsPath = Join-Path -Path $env:LOCALAPPDATA `
-ChildPath 'Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json'

# 如果是 Preview 版本
if (-not (Test-Path $settingsPath)) {
$settingsPath = Join-Path -Path $env:LOCALAPPDATA `
-ChildPath 'Packages\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\LocalState\settings.json'
}

# 备份原始配置
$backupPath = $settingsPath + '.backup'
Copy-Item -Path $settingsPath -Destination $backupPath -Force
Write-Host "已备份配置到: $backupPath"

# 读取并解析 JSON
$settings = Get-Content -Path $settingsPath -Raw | ConvertFrom-Json
Write-Host "当前共有 $(($settings.schemes | Measure-Object).Count) 个配色方案"

执行结果示例:

1
2
已备份配置到: C:\Users\dev\AppData\Local\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json.backup
当前共有 24 个配色方案

下面演示如何通过脚本添加自定义配色方案:

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
# 定义一个新的配色方案
$newScheme = @{
name = 'PowerShell Dark Modern'
background = '#0C0C0C'
foreground = '#CCCCCC'
cursorColor = '#00FF00'
black = '#0C0C0C'
blue = '#0037DA'
cyan = '#3A96DD'
green = '#13A10E'
purple = '#881798'
red = '#C50F1F'
white = '#CCCCCC'
yellow = '#C19C00'
brightBlack = '#767676'
brightBlue = '#3B78FF'
brightCyan = '#61D6D6'
brightGreen = '#16C60C'
brightPurple = '#B4009E'
brightRed = '#E74856'
brightWhite = '#F2F2F2'
brightYellow = '#F9F1A5'
}

# 将哈希表转换为 PSCustomObject 并添加到 schemes 数组
$schemeObj = [PSCustomObject]$newScheme

if (-not $settings.schemes) {
$settings | Add-Member -MemberType NoteProperty -Name 'schemes' -Value @()
}
$settings.schemes += $schemeObj

# 将指定 Profile 的配色方案设置为新建的方案
foreach ($profile in $settings.profiles.list) {
if ($profile.name -match 'PowerShell') {
$profile | Add-Member -MemberType NoteProperty -Name 'colorScheme' `
-Value 'PowerShell Dark Modern' -Force
Write-Host "已为 Profile '$($profile.name)' 应用新配色"
}
}

# 写回 settings.json
$settings | ConvertTo-Json -Depth 10 | Set-Content -Path $settingsPath -Encoding UTF8
Write-Host '配置已保存,重新打开 Terminal 即可看到效果'

执行结果示例:

1
2
3
已为 Profile 'Windows PowerShell' 应用新配色
已为 Profile 'PowerShell 7' 应用新配色
配置已保存,重新打开 Terminal 即可看到效果

在 Profile 中添加实用函数

一个精心定制的终端不仅仅是好看,更要好用。以下是一组可以直接加入 Profile 的实用函数,提升日常操作效率。

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
# 这段代码演示如何向 Profile 中批量添加实用函数
$functions = @(
@{
Name = 'Get-TerminalVersion'
Code = @'
function Get-TerminalVersion {
<# 获取当前 Windows Terminal 版本 #>
$pkg = Get-AppxPackage *WindowsTerminal*
if ($pkg) {
[PSCustomObject]@{
Name = $pkg.Name
Version = $pkg.Version
Status = '已安装'
}
} else {
Write-Warning '未检测到 Windows Terminal 安装'
}
}
'@
}
@{
Name = 'Export-TerminalSettings'
Code = @'
function Export-TerminalSettings {
<# 导出 Windows Terminal 配置到桌面 #>
$dest = Join-Path $env:USERPROFILE 'Desktop\wt-settings-backup.json'
$src = Join-Path $env:LOCALAPPDATA `
'Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json'
if (Test-Path $src) {
Copy-Item -Path $src -Destination $dest -Force
Write-Host "配置已导出到: $dest"
} else {
Write-Warning '未找到 Windows Terminal 配置文件'
}
}
'@
}
@{
Name = 'Set-TerminalOpacity'
Code = @'
function Set-TerminalOpacity {
<# 设置 Terminal 窗口透明度(需要 Terminal 设置中启用亚克力效果)#>
param([int]$Opacity = 80)
$settingsPath = Join-Path $env:LOCALAPPDATA `
'Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json'
$settings = Get-Content $settingsPath -Raw | ConvertFrom-Json
foreach ($p in $settings.profiles.list) {
$p | Add-Member -MemberType NoteProperty -Name 'opacity' `
-Value $Opacity -Force
}
$settings | ConvertTo-Json -Depth 10 | Set-Content $settingsPath -Encoding UTF8
Write-Host "已将所有 Profile 透明度设置为 ${Opacity}%"
}
'@
}
)

# 写入 Profile
$profileFile = $PROFILE.CurrentUserAllHosts
foreach ($func in $functions) {
if (-not (Select-String -Path $profileFile -Pattern "function $($func.Name)" -Quiet)) {
Add-Content -Path $profileFile -Value "`n$($func.Code)"
Write-Host "已添加函数: $($func.Name)"
} else {
Write-Host "函数已存在,跳过: $($func.Name)"
}
}

执行结果示例:

1
2
3
已添加函数: Get-TerminalVersion
已添加函数: Export-TerminalSettings
已添加函数: Set-TerminalOpacity

添加完成后,重新加载 Profile 即可使用这些函数:

1
2
3
4
. $PROFILE.CurrentUserAllHosts

# 测试获取 Terminal 版本
Get-TerminalVersion
1
2
3
Name                            Version        Status
---- ------- ------
Microsoft.WindowsTerminal 1.22.11141.0 已安装

注意事项

  1. 备份配置再修改:Terminal 的 settings.json 是唯一配置来源,修改前务必备份。本文中的脚本会自动创建 .backup 副本,但建议你也定期将配置纳入版本控制。

  2. Oh My Posh 字体依赖:大部分 Oh My Posh 主题需要 Nerd Font 字体才能正确显示图标。推荐安装 CascadiaCodeFiraCode 的 Nerd Font 版本,并在 Terminal 设置中将字体名填入 Profile 的 font.face 字段。

  3. Profile 执行策略:如果系统执行策略禁止运行脚本,Oh My Posh 和自定义函数都不会生效。需要以管理员身份执行 Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser 来放行本地脚本。

  4. JSON 序列化深度ConvertTo-Json 默认深度为 2,Terminal 的 settings.json 嵌套较深(特别是 actions 数组),务必使用 -Depth 10 或更高,否则部分配置会丢失。

  5. Preview 和 Stable 版本路径不同:Windows Terminal Preview 版的包名包含 Previewsettings.json 的路径也不同。脚本中应同时检测两个路径,避免修改错目标。

  6. 亚克力效果和透明度需要 GPU 支持useAcrylicopacity 设置依赖 GPU 加速渲染。在虚拟机或远程桌面会话中,这些视觉效果可能无法正常工作,但不影响核心功能使用。

PowerShell 技能连载 - Windows Terminal 与 PowerShell 环境配置

适用于 Windows 10/11,PowerShell 7.0 及以上版本

为什么终端环境如此重要

作为 PowerShell 用户,终端是每天打交道最多的工具。一个配置得当的终端环境不仅能让你心情愉悦,更能显著提升工作效率。试想一下:当你打开终端,迎接你的是清晰的配色、醒目的 Git 状态提示、智能的命令补全,和一系列顺手可用的自定义函数——是不是比面对默认的蓝底白字更有动力?

传统的 Windows PowerShell 5.1 控制台(conhost)功能有限,不支持多标签、缺乏自定义能力。而 Windows Terminal 的出现彻底改变了这一局面:它是开源的、高度可定制的、支持 GPU 加速渲染的现代终端应用。配合 PowerShell 7 和 Oh My Posh,我们可以打造一个不输 macOS/Linux 的终端体验。

本文将从 Windows Terminal 配置、PowerShell 7 Profile 定制、Oh My Posh 美化、PSReadLine 增强以及常用别名函数五个方面,带你一步步搭建理想的终端环境。

Windows Terminal 基础配置

Windows Terminal 的配置存储在一个 JSON 文件中,通过 Ctrl+Shift+, 可以快速打开。以下是一个经过优化的配置片段,涵盖默认配置文件、启动目录、字体和配色方案。

1
2
3
4
5
6
7
8
9
10
11
12
# 查看 Windows Terminal settings.json 的路径
$settingsPath = "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json"
Write-Host "Windows Terminal 配置文件路径: $settingsPath"

# 如果文件存在,读取当前默认配置
if (Test-Path $settingsPath) {
$settings = Get-Content $settingsPath -Raw | ConvertFrom-Json
$defaultProfile = $settings.profiles.list | Where-Object { $_.guid -eq $settings.defaultProfile }
Write-Host "当前默认配置文件: $($defaultProfile.name)"
} else {
Write-Host "未找到 Windows Terminal 配置文件,请确认已安装 Windows Terminal"
}

执行结果示例:

1
2
Windows Terminal 配置文件路径: C:\Users\dev\AppData\Local\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json
当前默认配置文件: PowerShell 7

settings.json 中,以下几项配置值得优先调整:

  • defaultProfile:设置为 PowerShell 7 的 GUID,确保默认打开的是 PS7
  • startingDirectory:设定为你常用的工作目录
  • font.face:推荐使用 Nerd Font,以支持 Oh My Posh 的图标显示
  • colorScheme:选择一个护眼且对比度适中的配色方案

PowerShell 7 Profile 定制

PowerShell 的 Profile 文件类似于 bash 的 .bashrc,每次启动 PowerShell 时自动执行。PS7 的 Profile 路径与 PS5.1 不同,互不干扰。

1
2
# 查看 PowerShell 7 的所有 Profile 路径
$PROFILE | Format-List -Force

执行结果示例:

1
2
3
4
AllUsersAllHosts       : C:\Program Files\PowerShell\7\profile.ps1
AllUsersCurrentHost : C:\Program Files\PowerShell\7\Microsoft.PowerShell_profile.ps1
CurrentUserAllHosts : C:\Users\dev\Documents\PowerShell\profile.ps1
CurrentUserCurrentHost : C:\Users\dev\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

最常用的是 CurrentUserCurrentHost,即当前用户、当前宿主的 Profile。我们来创建一个基础 Profile,包含环境变量设置和模块导入:

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
# 确保 Profile 目录存在
$profileDir = Split-Path $PROFILE -Parent
if (-not (Test-Path $profileDir)) {
New-Item -ItemType Directory -Path $profileDir -Force | Out-Null
Write-Host "已创建 Profile 目录: $profileDir"
}

# 创建初始 Profile 文件
$profileContent = @'
# === 环境变量 ===
$env:EDITOR = "code"
$env:PYTHONIOENCODING = "utf-8"

# === 编码设置 ===
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8

# === 常用模块自动导入 ===
$modulesToImport = @(
"PSReadLine",
"Terminal-Icons"
)

foreach ($mod in $modulesToImport) {
if (Get-Module -ListAvailable -Name $mod) {
Import-Module $mod -ErrorAction SilentlyContinue
}
}

Write-Host "Profile 加载完成 - $(Get-Date -Format 'HH:mm:ss')" -ForegroundColor DarkGray
'@

Set-Content -Path $PROFILE -Value $profileContent -Encoding UTF8
Write-Host "Profile 文件已写入: $PROFILE"

执行结果示例:

1
Profile 文件已写入: C:\Users\dev\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

这段 Profile 做了几件关键的事:统一了编码为 UTF-8(避免中文乱码)、设置了默认编辑器、自动导入常用模块。编码问题是 Windows 下开发最常遇到的坑之一,在 Profile 中统一处理可以省去大量排查时间。

Oh My Posh 终端美化

Oh My Posh 是 Windows 上最流行的终端提示符美化工具,它能为你的终端添加 Git 状态、语言版本、执行时间等信息。配合 Nerd Font,还可以显示丰富的图标。

1
2
3
4
5
6
7
8
9
# 安装 Oh My Posh(推荐通过 winget)
winget install JanDeDobbeleer.OhMyPosh -s winget

# 安装推荐的 Nerd Font
oh-my-posh font install FiraCode

# 查看可用的主题列表
oh-my-posh get shell
Write-Host "可用主题数量: $((oh-my-posh get themes).Count)"

执行结果示例:

1
2
3
4
已成功安装 Oh My Posh
正在安装 FiraCode Nerd Font...
已完成字体安装
可用主题数量: 87

在 Profile 中初始化 Oh My Posh。注意不要在 here-string 中使用三反引号,我们用变量拼接的方式来设置主题路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 将 Oh My Posh 初始化代码添加到 Profile
$ompInit = @'
# === Oh My Posh 初始化 ===
if (Get-Command oh-my-posh -ErrorAction SilentlyContinue) {
$ompTheme = Join-Path $env:POSH_THEMES_PATH "jandebuhr.omp.json"
if (-not (Test-Path $ompTheme)) {
$ompTheme = "jandedeurbel"
}
oh-my-posh init pwsh --config $ompTheme | Invoke-Expression
}
'@

# 追加到 Profile 末尾
Add-Content -Path $PROFILE -Value "`n$ompInit" -Encoding UTF8
Write-Host "Oh My Posh 初始化代码已添加到 Profile"

执行结果示例:

1
Oh My Posh 初始化代码已添加到 Profile

Oh My Posh 的主题文件是 JSON 格式,你也可以自定义主题。推荐先用内置主题找到喜欢的风格,再在此基础上微调颜色和段落的显示顺序。一个好主题应该在美观和信息密度之间取得平衡——信息太少不实用,太多则显得杂乱。

常用别名与自定义函数

Profile 中最实用的部分之一就是定义别名和函数,把日常高频操作压缩成简短的命令。以下是笔者常用的配置:

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
# 定义常用别名和函数
$aliasAndFuncs = @'
# === 常用别名 ===
Set-Alias -Name ll -Value Get-ChildItem
Set-Alias -Name which -Value Get-Command
Set-Alias -Name touch -Value New-Item
Set-Alias -Name cat -Value Get-Content

# === 自定义函数 ===

# 快速进入项目目录
function projects {
Set-Location "D:\Projects"
}

# 快速查看端口占用
function port {
param([int]$PortNumber)
Get-NetTCPConnection -LocalPort $PortNumber -ErrorAction SilentlyContinue |
Select-Object LocalPort, OwningProcess, State |
Format-Table -AutoSize
}

# 快速查看系统信息
function sysinfo {
$os = Get-CimInstance Win32_OperatingSystem
$cpu = Get-CimInstance Win32_Processor
$mem = [math]::Round($os.FreePhysicalMemory / 1MB, 2)
[PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
OS = $os.Caption
CPU = $cpu.Name
FreeMem_GB = $mem
Uptime = (Get-Date) - $os.LastBootUpTime
} | Format-List
}

# Git 快捷操作
function gs { git status }
function gl { git log --oneline -15 }
function gp { git push }
function gd { git diff $args }

# Docker 快捷操作
function dps { docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Ports}}" }
function dex {
param([string]$ContainerName)
docker exec -it $ContainerName /bin/sh
}
'@

Add-Content -Path $PROFILE -Value "`n$aliasAndFuncs" -Encoding UTF8
Write-Host "别名和函数已添加到 Profile"

执行结果示例:

1
别名和函数已添加到 Profile

这些函数的设计原则是”短小精悍”:每个函数只做一件事,名字尽量简短但能望文生义。比如 port 8080 比输入完整的 Get-NetTCPConnection -LocalPort 8080 简洁得多,gs 则是 git status 的经典缩写。你可以根据自己的工作流继续扩展这个列表。

PSReadLine 增强补全

PSReadLine 是 PowerShell 的命令行编辑模块,PS7 已内置 2.x 版本。合理配置后,它能提供类似 fish shell 的自动补全体验——输入时实时预测、历史命令搜索、语法高亮一应俱全。

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
# 配置 PSReadLine
$psreadlineConfig = @'
# === PSReadLine 配置 ===
if ($host.Name -eq 'ConsoleHost') {
# 预测文本来源:历史记录 + 插件
Set-PSReadLineOption -PredictiveSource HistoryAndPlugin
# 预测文本显示方式:列表视图(类似 fish)
Set-PSReadLineOption -PredictiveStyle ListView
# 历史记录去重
Set-PSReadLineOption -HistoryNoDuplicates
# 保存历史记录条数
Set-PSReadLineOption -MaximumHistoryCount 10000
# 历史记录保存路径
Set-PSReadLineOption -HistorySavePath (Join-Path $env:USERPROFILE ".ps_history")
# 编辑模式设为 Emacs(更符合开发习惯)
Set-PSReadLineOption -EditMode Emacs
# 颜色主题
Set-PSReadLineOption -Colors @{
Command = 'Yellow'
Parameter = 'Green'
String = 'Cyan'
Comment = 'DarkGray'
Operator = 'Magenta'
Prediction = 'DarkGray'
InlinePrediction = 'DarkGray'
}

# Ctrl+d 删除字符(类似 bash)
Set-PSReadLineKeyHandler -Chord 'Ctrl+d' -Function DeleteChar
# Ctrl+w 删除前一个单词
Set-PSReadLineKeyHandler -Chord 'Ctrl+w' -Function BackwardKillWord
# 上下箭头在预测列表中导航
Set-PSReadLineKeyHandler -Chord UpArrow -Function HistorySearchBackward
Set-PSReadLineKeyHandler -Chord DownArrow -Function HistorySearchForward
# Tab 补全显示菜单
Set-PSReadLineKeyHandler -Chord Tab -Function MenuComplete
Set-PSReadLineKeyHandler -Chord Shift+Tab -Function Complete
}
'@

Add-Content -Path $PROFILE -Value "`n$psreadlineConfig" -Encoding UTF8
Write-Host "PSReadLine 配置已添加到 Profile"

执行结果示例:

1
PSReadLine 配置已添加到 Profile

PSReadLine 的预测补全功能是提升效率的关键。当你开始输入命令时,它会根据历史记录实时显示匹配建议,按右箭头即可接受。ListView 模式下还会显示一个下拉列表供你选择,配合 HistoryAndPlugin 来源,补全的准确度非常高。

完整 Profile 模板

经过以上各部分的配置,下面给出一个完整的 Profile 模板,方便你一次性部署。建议按需修改目录路径和主题名称。

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
# 生成完整 Profile 模板
$templateContent = @'
# ============================================
# PowerShell 7 Profile - 完整模板
# 生成日期: 2025-04-25
# ============================================

# === 编码设置 ===
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8

# === 环境变量 ===
$env:EDITOR = "code"

# === Oh My Posh ===
if (Get-Command oh-my-posh -ErrorAction SilentlyContinue) {
$ompTheme = Join-Path $env:POSH_THEMES_PATH "jandebuhr.omp.json"
if (-not (Test-Path $ompTheme)) { $ompTheme = "jandedeurbel" }
oh-my-posh init pwsh --config $ompTheme | Invoke-Expression
}

# === 模块导入 ===
$autoModules = @("Terminal-Icons")
foreach ($m in $autoModules) {
if (Get-Module -ListAvailable -Name $m) {
Import-Module $m -ErrorAction SilentlyContinue
}
}

# === PSReadLine ===
if ($host.Name -eq 'ConsoleHost') {
Set-PSReadLineOption -PredictiveSource HistoryAndPlugin
Set-PSReadLineOption -PredictiveStyle ListView
Set-PSReadLineOption -HistoryNoDuplicates
Set-PSReadLineOption -MaximumHistoryCount 10000
Set-PSReadLineOption -HistorySavePath (Join-Path $env:USERPROFILE ".ps_history")
Set-PSReadLineOption -EditMode Emacs
Set-PSReadLineOption -Colors @{
Command = 'Yellow'
Parameter = 'Green'
String = 'Cyan'
Comment = 'DarkGray'
Prediction = 'DarkGray'
InlinePrediction = 'DarkGray'
}
Set-PSReadLineKeyHandler -Chord UpArrow -Function HistorySearchBackward
Set-PSReadLineKeyHandler -Chord DownArrow -Function HistorySearchForward
Set-PSReadLineKeyHandler -Chord Tab -Function MenuComplete
}

# === 别名 ===
Set-Alias ll Get-ChildItem
Set-Alias which Get-Command
Set-Alias touch New-Item

# === 自定义函数 ===
function projects { Set-Location "D:\Projects" }
function port([int]$p) { Get-NetTCPConnection -LocalPort $p -ErrorAction SilentlyContinue | Format-Table -AutoSize }
function sysinfo {
$os = Get-CimInstance Win32_OperatingSystem
[PSCustomObject]@{
Computer = $env:COMPUTERNAME
OS = $os.Caption
FreeMemGB = [math]::Round($os.FreePhysicalMemory / 1MB, 2)
Uptime = (Get-Date) - $os.LastBootUpTime
} | Format-List
}

# === Git 快捷命令 ===
function gs { git status }
function gl { git log --oneline -15 }
function gp { git push }

Write-Host "Profile loaded - $(Get-Date -Format 'HH:mm:ss')" -ForegroundColor DarkGray
'@

# 写入 Profile
$profileDir = Split-Path $PROFILE -Parent
if (-not (Test-Path $profileDir)) {
New-Item -ItemType Directory -Path $profileDir -Force | Out-Null
}
Set-Content -Path $PROFILE -Value $templateContent -Encoding UTF8
Write-Host "完整 Profile 模板已写入: $PROFILE"

执行结果示例:

1
完整 Profile 模板已写入: C:\Users\dev\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

部署完成后,重新打开 Windows Terminal 或执行 . $PROFILE 即可生效。首次加载时 Oh My Posh 会编译主题缓存,之后启动速度很快。如果遇到图标显示为方块,说明 Nerd Font 未正确安装或 Windows Terminal 未配置使用该字体。

注意事项

  1. Profile 加载顺序:Oh My Posh 初始化应放在 PSReadLine 配置之前,否则预测补全的颜色设置可能被覆盖。
  2. Nerd Font 必须在 Windows Terminal 中指定:仅安装字体不够,还需要在 settings.json 的 profile 中设置 "font": { "face": "FiraCode Nerd Font" }
  3. Profile 修改后别忘重载:修改 Profile 后,在当前会话中执行 . $PROFILE 重新加载,避免关闭重开。
  4. 编码一致性:确保 settings.json、Profile 文件和系统区域设置都使用 UTF-8,否则中文可能显示为乱码。
  5. 备份你的配置:建议将 Profile 文件和 settings.json 纳入 Git 管理,换机器时一键恢复。
  6. PSReadLine 插件模式PredictiveSource HistoryAndPlugin 需要安装补全模块(如 CompletionPredictor),如果未安装则回退到纯历史记录模式,不会报错。