Since I switched to Windows last year I'm using Microsoft's Powershell as my main shell environment (on my personal laptop). At first I didn't really look into Powershell's feature until around half a year ago when I found the motivation to work on a nicer prompt and spent some time migrating my ZSH profile to Powershell. I ended up creating a different profile that suits my needs, and that I had lot of fun building.
That's how my prompt looks:
It has the following features:
That was a cool and fun way to learn more about Powershell features, after spending all my life with sh, bash, and zsh it was refreshing to try out a different way to write shell scripts.
That's a very common feature in other shells, you often have environment variables you can set to display some information on the left and/or right sides of the prompt. In the case of Powershell I had to implement my own solution.
# Renders a 2 columns layout, with $left argument written to the extreme left of the console, and $right to the extreme
# right. The cursor is restored back to its position thus should not be impacted.
function Write-Layout ($left, $right) {
Write-Host $left -NoNewline
$origX = $Host.UI.RawUI.CursorPosition.X
$origY = $Host.UI.RawUI.CursorPosition.Y
$rightX = $Host.UI.RawUI.WindowSize.Width - (Length-Without-ANSI $right)
$rightY = $Host.UI.RawUI.CursorPosition.Y
$position = New-Object System.Management.Automation.Host.Coordinates $rightX, $rightY
$Host.UI.RawUI.CursorPosition = $position
Write-Host $right -NoNewLine
$position = New-Object System.Management.Automation.Host.Coordinates $origX, $origY
$Host.UI.RawUI.CursorPosition = $position
}
It took me quite some time to find out what were the correct objects to use, the documentation was a bit sparse on this. So far I haven't found any problem when using it but I may have overlooked some issues.
I didn't want to install external dependencies for something as simple and stable as escape code formatting, I took the full list of colors and styles and created enums for them.
The results in a simple file that can be copy pasted around if in need for other projects: formatting.ps1. It should cover all the escape codes. I also added functions to make it nicer to format text and test styles and colors directly from a shell.
This feature may only make sense in the context of my personal shell as I know exactly what to expect. I documented the function that implements the selection of the "main" directory fairly well, but I can paraphrase it here. I call "current main directory" a contextual parent directory that helps me understand what the current working directory is about, while avoiding displaying the complete path. The idea is to have something as terse as possible that still gives enough context to understand where I am.
As an example, if I am in C:\Users\Sam\Development\neocolor
, everything until Development\neocolor
is redundant. I spend almost all my time in the Development
directory so I know exactly where to find it. In this case, the "main directory" is Development
while the current directory is neocolor
.
The possible values are:
OneDrive\{firstSubdir}
if I am deep in a OneDrive subdirectory. e.g: OneDrive\Workspace
when the full path is C:\Users\Sam\OneDrive\Workspace\Games\2D Tutorial
OneDrive
if I am in a subdirectory of the OneDrive location{firstSubdir}
if I am deep in a subdirectory branch of the $HOME
directory. e.g: Anayconda3
when the full path is C:\Users\Sam\Anaconda3\include\internal
Users\Sam
if I am in a subdirectory of the $HOME
directoryC:\Users
when I am in C:\Users\Sam
C:
in that caseThat seems to be a lot of rules when I describe them, but in practice I don't memorize them, the prompt displays just enough of the current context to let me know exactly where I am. And if I don't then pwd
is never far away.
The simplest way I found is this one:
# Get the current git branch name
function Git-Branch {
$raw = git status --porcelain --branch
$branch = $raw.Split(" ").Split("...")[1]
return $branch
}
No issue found so far.
I want admin shell to be marked as such in a visual way. If I detect an administrator role (Windows case) or a root user (Linux and macOS cases), I add an Admin ::
marker on the left.
# Check if current user is an administrator
function Test-Administrator {
if ($IsLinux -or $IsMacOS) {
return ((id -u) -eq 0)
}
if ($IsWindows) {
$user = [Security.Principal.WindowsIdentity]::GetCurrent()
return ([Security.Principal.WindowsPrincipal] $user).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
return ""
}
For some reasons it is mostly unknown by Linux users that Powershell is multiplatform and can be used as the main shell (or just a runtime for shell scripts) on Windows, Linux, and macOS. It's a bit of shame given that Powershell is quite capable and has way less ugly edge cases that make shells from the sh
family really difficult to learn and use correctly. When checking for platform specific details, such as some specific paths, it's trivial to assert what platform we are on using $IsLinux
, $IsWindows
, and $IsMacOS
.
In addition to the general prompt, I implemented a more experimental way to interact with the shell that breaks some expected user experience and expectations. I call it the zen mode, and it looks like this.
It's a bit of an oddity, I don't think I've ever seen a shell behaving like this. The picture doesn't really communicate the experience of using the zen mode so here is a short video and explanation:
The idea is to have a shell that doesn't display a prompt at all and is as minimalistic as possible. It only shows you the current command and its output, has no other information. Why would you use such a mode? To be honest, I don't know, I was working on my prompt, then realized that I often don't want to have more details than just what I'm doing right now. In practice I haven't found myself using it that much 🤷♀️ but I see it as an interesting experiment.