折腾 oh-my-posh 的那些事

让你的 PowerShell 拥有 zsh 般的体验

为什么要折腾 oh-my-posh?

要数在我前几年刚用 linux 时候最让我大开眼界的事物之一,当时是真的被 kali 的终端帅到,至今也还是觉得有那样一个舒适的终端是一件很幸福的事情。最开始甚至不知道 zsh 是什么、也不知道为什么我 wsl 安装的 kali 就没有那样的效果——直到某次我偶然了解到了 zsh,在 wsl 中装好后,又把虚拟机里的 ~/.zshrc 拷贝进 wsl——才有了那样的效果。

之后偶然看见了 oh-my-posh,颜值是第一生产力,便开始了一番折腾之路。况且比起来 cmd.exe,用 PowerShell 的体验不知道高到哪里去了……

可能是在 cmd.exe 等劣质终端中被长期使用的原因,这个文件失去了一些重要成分,变成了一堆乱码,也不会再显示出 flag 了。

—— Hackergame 2021

安装 PowerShell

虽然现在的 Windows 普遍自带着 PowerShell,但是其版本早已过时了,笔者写此篇文章的时候已经更新到了 PowerShell 7.2.0 ,相比于原来版本的有了很多新功能,基于 C# 和 .NET 6 ,也支持全平台。

你可以在 Github Release 找到最新的版本,并下载安装。

有关 PowerShell 的版本差距和新的内容这里不作为重点讨论,但就作为计算器而言,也是个不错的工具了,甚至能和 python 一样作为很顺手的计算器(bushi

安装 Windows Terminal

既然要追求颜值,当然少不了微软最新的这个终端了!

在这里下载 => Windows Terminal

就算你不用 PowerShell,不用 oh-my-posh,单就这个终端,已经是对体验的大大提升了,它在 Win11 中自带,不妨尝试一下。

挑一个喜欢的 Mono 字体

就我个人而言,我认为 JetBrains Mono 是真的很棒,在之前我一直用 Consolas,但自从第一次使用后就全局换上了它,你可以在 JetBrains Mono 找到并下载到它。

如果你足够细心,也会发现本站全部西文字体均使用它作为默认字体,很幸运地,Google Fonts 提供了相关在线资源,你可以从这里找到 JetBrains Mono - Google Fonts

同时,仅仅这样修改了字体,在你继续使用 oh-my-posh 的时候还是可能遇到一些显示问题,这是因为你需要一种叫做 Nerd Fonts 的东西,你可以在这里找到很多字体的 Nerd 版本:Nerd Fonts

安装 oh-my-posh 和 posh-git

使用如下的指令安装:

Install-Module oh-my-posh -Scope CurrentUser
Install-Module posh-git -Scope CurrentUser

一句 hez2010 的评论:

oh-my-posh 建议用 2.x 版本,2.x 版本是 PowerShell 写的,但是 3.0 开始该插件换成用 Golang 实现了,不仅体积大了十几 mb,样式还变得更难看,速度也更慢了。
(其实这里想吐槽一下,用 Go 去写 PowerShell 脚本的插件怕不是作者脑子进水了。作者觉得用 Go 写的话 oh-my-posh 就可以给其他 shell 用了,但问题在于 bash 和 zsh 用户都有自己的 oh-my-* 实现,犯不着去用 oh-my-posh)

就我目前的观察而言, oh-my-posh 的更新还是很快的,虽然我还是很想吐槽为什么要用 Go 去写,做下文配置的时候还想着干脆自己用 C# 重制一个算了……况且 .NET 6 也能全平台了不是(

安装完成后需要更改 $Profile,此文件类似于 ~/.zshrc 会在启动 PowerShell 的时候自动执行,我们要用它引入 oh-my-posh 模块:

notepad.exe $Profile

输入下列内容:

Import-Module posh-git # 引入 posh-git
Import-Module oh-my-posh # 引入 oh-my-posh

Set-PoshPrompt -Theme Paradox # 设置主题为 Paradox

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

Set-PSReadlineKeyHandler -Key Tab -Function Complete # 设置 Tab 键补全
Set-PSReadLineKeyHandler -Key "Ctrl+d" -Function MenuComplete # 设置 Ctrl+d 为菜单补全和 Intellisense
Set-PSReadLineKeyHandler -Key "Ctrl+z" -Function Undo # 设置 Ctrl+z 为撤销

Set-PSReadLineKeyHandler -Key UpArrow -ScriptBlock {
[Microsoft.PowerShell.PSConsoleReadLine]::HistorySearchBackward()
[Microsoft.PowerShell.PSConsoleReadLine]::EndOfLine()
} # 设置向上键为后向搜索历史记录,并将光标移动到行尾

Set-PSReadLineKeyHandler -Key DownArrow -ScriptBlock {
[Microsoft.PowerShell.PSConsoleReadLine]::HistorySearchForward()
[Microsoft.PowerShell.PSConsoleReadLine]::EndOfLine()
} # 设置向下键为前向搜索历史纪录,并将光标移动到行尾

不过这只是一个 demo,主题、快捷键和对应的功能是完全可以自己修改的,你可以将他们设置为你喜欢的样子,设置好后保存,重启 PowerShell 加载配置。

自定义主题

至此,oh-my-posh 就安装结束了,你可以使用 Get-PoshThemes 预览全部默认主题,或者访问 Themes 查看全部的内置主题。

只是……我觉得他们都不好看,见过了 kali 的我怎么能忍受这样的主题呢……要么花里胡哨,要么显示着不需要的信息,要么想要的信息又没有显示……

于是干脆导出了一下当前的主题,配合着文档研究了一下怎么写主题,将它导出到某个目录下,我这里用 %USERPROFILE%/.kali.omp.json 为例。你可以找个自己喜欢的目录,并且新建一个自定义主题的文件,之后将 $Profile 中的指定主题的条目更高一下,这里最好使用绝对路径,以免出现一些奇怪的问题。:

Set-PoshPrompt -Theme /path/to/.kali.omp.json # 设置主题为自定义主题

这就是经过我修改的主题配置的效果了,考虑到使用 anaconda,也额外加了 python 环境的显示:

之后附上我编辑的 kali 主题的配置文件,相关配置说明可以在 oh-my-posh docsConfiguration 以及 Segments 段找到。由于考虑到性能和使用频率的原因,我并没有将连接符在 Root 模式下变为蓝色,这并不是代表着不可以,只是感觉没有必要,读者好奇可以自己改一改试试。

不过也就是在配置的过程中更增长了我想要使用 C# 将其重写的想法,这我很想吐槽一番

GitHub 的源码处,可见每个 Segment 都独立成为一个文件,并且和 test 直接扔在同一个文件夹,为什么不把它们独立成文件夹呢?测试为什么不分离呢……虽然这些属于规范上的问题,也可能受到 Go 的影响,毕竟程序能跑也就无功无过吧。

但是这些 Segment 有些支持 Template 有些不支持,并且在 PrefixPostfix 属性中不能使用模版字符串、看似有规律又没有规律、看似统一却又各自为政的段落配置设计着实让人难受,以至于如果我想实现 Root 模式下蓝色主色调的话,需要增加不少文本段落——但这些段落又无法根据段段有无而选择是否显示……

我转而看到了它所说的调色板功能,然而在看完之后让我大失所望,本想着配置内部可以写一个颜色的标签,像博客的黑暗模式一样在不同情况下对应不同颜色,然而它并不支持……

……细数完这些之后,我真的更想自己实现一遍了(逃

另外,关于饱受诟病的加载时间问题……我 anaconda 加载都需要那么久了,也不差它这点时间了,谁让颜值是第一生产力呢(

附:主题配置文件,也可以在 Github Gist 处找到。笔者认为稍微看看应该能明白相关的显示效果说如何实现的,就不写注释了。 希望你喜欢!

{
  "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
  "blocks": [
    {
      "alignment": "left",
      "segments": [
        {
          "type": "session",
          "style": "plain",
          "foreground": "lightBlue",
          "foreground_templates": [
            "{{ if .Root }}red{{ end }}"
          ],
          "properties": {
            "display_host": true,
            "prefix": "<green>┌──(</>",
            "postfix": "<green>)</>",
            "template": "{{ if .Root }}root💀{{ else }}{{ .UserName }}@{{ end }}{{ .ComputerName }}"
          }
        },
        {
          "foreground": "yellow",
          "properties": {
            "prefix": "<green>-[</>\uE235 ",
            "postfix": "<green>]</>",
            "display_version": false,
            "display_virtual_env": true
          },
          "style": "plain",
          "type": "python"
        },
        {
          "foreground": "lightWhite",
          "properties": {
            "folder_separator_icon": "<#c0c0c0>/</>",
            "style": "full",
            "prefix": "<green>-[</>",
            "postfix": "<green>]</>"
          },
          "style": "plain",
          "type": "path"
        },
        {
          "foreground": "white",
          "properties": {
            "prefix": "<green>-[</>",
            "postfix": "<green>]</>",
            "template": "{{ .HEAD }}"
          },
          "style": "plain",
          "type": "git"
        }
      ],
      "type": "prompt"
    },
    {
      "type": "prompt",
      "alignment": "right",
      "segments": [
        {
          "type": "executiontime",
          "style": "plain",
          "foreground": "white",
          "properties": {
            "style":"round",
            "always_enabled": true
          }
        },
        {
          "type": "exit",
          "style": "plain",
          "foreground": "green",
          "foreground_templates": [
            "{{ if gt .Code 0 }}red{{ end }}"
          ],
          "properties": {
            "always_enabled": true,
            "template": "{{ if gt .Code 0 }}⨯{{else}}✓{{ end }}"
          }
        }
      ]
    },
    {
      "alignment": "left",
      "newline": true,
      "segments": [
        {
          "foreground": "lightBlue",
          "properties": {
            "prefix": "<green>└─</>",
            "text": "{{ if .Root }}<red>#</>{{ else }}${{ end }}"
          },
          "style": "plain",
          "type": "text"
        }
      ],
      "type": "prompt"
    }
  ],
  "final_space": false
}

Reference

  1. 给 PowerShell 带来 zsh 的体验 - hez2010 - 知乎
  2. JanDeDobbeleer/oh-my-posh - Github
  3. oh-my-posh documentation