Anatomy of a Prompt (PowerShell)

First posted: 20 August 2017
Last updated: 20 August 2022

Table of Contents

History

Introduction

Over the years, I've customized my prompt to maximize the information available to me while doing software development. Today it looks like this:

Windows Terminal (dark)

I also have a light variant:

Windows Terminal (light)

There's a lot going on in the screenshots, so I'm going to put together the pieces. The information here assumes you have general familiarity with customizing your PowerShell environment (for example, editing your profile file). It also assumes you have some pre-requisites installed, like Git for Windows and Visual Studio.

You can copy/paste along as we go, but I have also provided downloadable versions at the end of the blog post.

Important Note: When saving the PowerShell files, make sure they are saved as "UTF-8 with Signature". This will ensure that PowerShell treats your file as UTF-8 and does not accidentally treat it as ANSI. Here is an example using my editor of choice, Notepad2:

Encoding in Notepad2

Fonts and Colors

In the original version of this blog post, I described a terminal program I used to use called ConEmu. I've since switched to Windows Terminal as it provides better compatibility with Windows Subsystem for Linux v2.

The prompt uses several special characters that are not part of a standard font (for example, the Git branch symbol in the posh-git banner). The prompt assumes that you have installed and are using one the Nerd Fonts; the screenshots shown here are done with the Iosevka Nerd Font.

The Windows Terminal application uses a configuration file. Hit "ctrl comma", and it will launch a text editor for you to edit the configuration file. This is the dark and light theme:

The full files (including the Ubuntu icon file that I use) are available here: https://gist.github.com/bradwilson/3b81a192a63056e310e453bc7f4c063d.

posh-git

The blue portion of the prompt is Git project information, powered by posh-git. This is a PowerShell plugin that supports printing information about the Git repository referenced by the current directory. The great thing about posh-git is that it comes with a tremendous amount of flexibility when formatting the prompt output. Here is what it looks like by default:

Default posh-git Prompt

You configure the prompt by overriding the default values in a configuration object that posh-git sets. Here are the overrides which convert that default prompt into the blue prompt you've seen above:

To use these overrides, place these lines after the Import-Module statement that posh-git added to your PowerShell profile. Once you restart your shell, you should see your new customizations in place (don't worry about the path; we'll be re-styling that later on):

posh-git Customized Prompt

If you want to customize the symbols used in the prompt, the easiest way to find them is by using the Character Map application built into Windows. Just select the Nerd Font you're using while browsing characters, and you can copy them directly from this app into your profile:

Windows Character Map application

prompt Function

When PowerShell wants to display your prompt, it executes the command prompt. By default, this command is provided by a "PowerShell function". Adding this to your Profile will override the default prompt with our custom prompt (I'll break down each meaningful section afterward):

The findHereOrParent function is used in a few of the prompt pieces (printing the versions of Go, Rust, and the .NET SDK) so that the prompt piece will only be shown when you're in a folder of a project that uses the specific language. The function itself is fairly straight forward: it looks for any of the given file paths (which supports wildcards) in the current folder or any of the parent folders:

The try/catch here is because some filesystem providers may throw errors when running Get-ChildItem (for example, the registry provider does not support the use of -Filter). We catch and swallow the error so that it's not shown.

Let's break this down into the individual parts of the prompt.

Showing errors Error display

The first section of the prompt (red background) is where we optionally show errors that have occurred:

There are two categories of errors that might happen in PowerShell: internal errors (for example, you try to run a command that doesn't exist), and external errors (when you run an executable, and the return code is something other than zero). We have separate code for each kind. We reset the $Error array and the $LASTEXITCODE values here when we're done. Additionally, we detect the "special" exit code of -1073741510 (also known as 0xC000013A) which indicates that the user pressed Ctrl+C to forcefully terminate the application.

Showing custom prompt environments Custom prompt environment

Next you'll see us printing out custom prompt environments:

This is used by things like the vs2022.ps1 script that we will discuss later. The assumption is that any text you place into $PromptEnvironment will be shown here.

Showing the Go version Go version

We print the version of Go that's being used, but only do so when in a folder with a Go project (this is determined by calling findHereOrParent go.mod). The version is determined by calling go version and parsing the result:

Showing the Rust version Rust version

We print the version of Rust that's being used, but only do so when in a folder with a Go project (this is determined by calling findHereOrParent Cargo.toml). The version is determined by calling rustc --version and parsing the result:

Showing the .NET SDK version .NET SDK version

We print the version of the .NET SDK that's being used, but only do so when in a folder with a .NET project (this is determined by calling findHereOrParent *.sln,*.csproj; if you use other language types like F# or VB.NET, you can add those file types as well). The version is determined by calling dotnet --version; this will track SDK versions via global.json, falling back to the latest installed version when not overridden.

Showing the Kubernetes (kubectl) current context Kubernetes (kubectl) context

The gray box in my prompt shows your current kubectl context by calling kubectl config current-context:

Showing Azure CLI current subscription Azure CLI current subscription

I find it useful to know which Azure subscription is my current subscription when running commands via az, so I print out the name of the subscription in my prompt:

Two important notes:

Showing Git information Git information

As we previously saw, posh-git does all the heavy lifting for figuring out the current Git status: it shows the current branch name as well as the state of the filesystem (modified files, un-tracked files, etc.). All we need to do is print the info:

Showing current path Current path

The last element of the prompt is the current directory:

The location is normalized to replace your home folder with a tilde, for shorter paths.

Printing directory stack depth

At the end of the prompt, we print one cyan + symbol for every level deep you are in the directory stack (that is, for every un-popped directory you pushed using Push-Location, aka pushd):

Conditional prompt text & color

I like to know when I'm running as admin or not, and whether my current window is using PowerShell or PowerShell Core.

There is a little bit of Windows vs. Unix-ism here to determine whether you're an admin. On Windows, we ask the current user identity whether it's in the Administrators group or not; on Unix-like environments, we test the user ID against 0 (meaning, you're root). Then when we print the prompt, we use green for (safe) non-admin users, and red for (unsafe) administrators.

The prompt text itself is either PS> for PowerShell, or pwsh> for PowerShell Core (regardless of the OS that you're running on).

Prompt Environment

The final piece of the puzzle is setting the prompt environment. I have several commands which place the shell into a special mode to be used for specific environments. For example, I have a command called vs2022 which adds Visual Studio environment variables just as though you'd run the Visual Studio Command Prompt, and then adds that information to the printed prompt environment.

I use a single script which can be used for any version of Visual Studio 2017 (or later):

And then I use a few simpler commands to invoke it:

vs.ps1 is a little complex because Visual Studio 2017+ now allows you to install multiple editions side by side. It looks to see which edition you have installed and uses the VsDevCmd.bat to get the updated environment variables. If you have multiple editions installed, you can use the -edition switch to specify which environment you want. In addition, Visual Studio 2022+ is now 64-bit, so we are forced to look in both 32- and 64-bit Program Files.

The final line of the script sets the global PromptEnvironment variable, which we use in our custom prompt function (as shown previously).

Downloadable Files

You can download profile.ps1 and vs.ps1 (and friends) from my GitHub gist.

Conclusion

I hope this quick tour through my custom prompt inspires you to make customizations of your own, based on your needs. Being able to carefully craft your prompt to be succinct and quickly glanceable can be very handy when spending significant time in your command window of choice.

Happy hacking!