Customize powershell

Notes published the
9 - 11 minutes to read, 2222 words

In a previous article I already wrote that I changed some settings of PowerShell, by making it behave more similar to a Unix shell.

As PowerShell is preinstalled in all current Windows versions (since Windows 7), it makes sense to try to use it as the default shell compared to cmd. When enabling ssh on Windows (without something like Cygwin), I also set it as the default shell for ssh. In the worst-case scenario, it is always possible to invoke cmd from Powershell.

Note 📝
There are two versions of PowerShell. The first one is the Windows component, also known as "Windows Powershell", which comes bundled with Windows, and PowerShell Core, which is also available in other operating systems. In those notes, I am addressing only the bundled version of Powershell, and Powershell is a shorthand for "Windows PowerShell".

Issues

Every time I invoke powershell there are some things that annoy me.

  • The blue background causes to mee too much eye strain, I prefer soft-dark backgrounds that do not flash too much light in my face

  • The blinking cursor

  • The prompt

  • The logo and hint that you can download a newer version of Powershell

  • You can scroll forward, even if there is no output

  • I cannot paste commands with Ctrl+⇧ Shift+V

  • Sometimes a long startup time

Granted, some issues are of the terminal emulator, and not of PowerShell. I’ve listed here everything together, as this is about using not using external programs, otherwise one could use another terminal emulator.

Those notes are minimal changes, with no external dependencies. If I’m going to install some external programs, why not install a different shell or terminal emulator?

A minimal profile file

My $PROFILE.AllUsersAllHosts file
Set-PSReadLineOption -EditMode Emacs;

Set-PSReadLineKeyHandler -Key Ctrl+Shift+C -Function Copy;
Set-PSReadLineKeyHandler -Key Ctrl+Shift+V -Function Paste;


Remove-Item -ErrorAction SilentlyContinue Alias:ls;
function ls([string] $dir=".") { (Get-ChildItem $dir).Name; }
Set-Alias -Name ll -Value Get-ChildItem;
Set-Alias -Name l -Value ls;
function la { (Get-ChildItem -Force).Name; }

Remove-Item -ErrorAction SilentlyContinue Alias:pwd;
function pwd { (Get-Location).Path.Replace("\", "/"); }

function touch([string] $filename){ if (!(Test-Path $filename)) { New-Item -Path $filename -ItemType File; }; }

if((Test-Path "C:/Program Files/Neovim/bin/nvim.exe") -and !(Get-Command "nvim" -ErrorAction SilentlyContinue)){
  Set-Alias -Name nvim -Value "C:/Program Files/Neovim/bin/nvim.exe";
}

if(Get-Command "nvim" -ErrorAction SilentlyContinue){
  Set-Alias -Name editor -Value nvim;
  Set-Alias -Name vim    -Value nvim;
  Set-Alias -Name vi     -Value nvim;
  Set-Alias -Name e      -Value nvim;
}elseif(Get-Command "vim" -ErrorAction SilentlyContinue){
  Set-Alias -Name editor -Value vim;
  Set-Alias -Name vi     -Value vim;
  Set-Alias -Name e      -Value vim;
}elseif(Get-Command "vi" -ErrorAction SilentlyContinue){
  Set-Alias -Name editor -Value vi;
  Set-Alias -Name e      -Value vi;
}

if(Get-Command "notepad++" -ErrorAction SilentlyContinue){
  Set-Alias -Name npp -Value "notepad++";
}elseif(Test-Path "C:/Program Files/Notepad++/notepad++.exe"){
  Set-Alias -Name "notepad++" -Value "C:/Program Files/Notepad++/notepad++.exe";
  Set-Alias -Name npp         -Value "C:/Program Files/Notepad++/notepad++.exe";
}

function prompt {
  $esc = [char]27;
  return "$esc[32m$env:UserName" + " $pwd".Replace(" $home", " ~").Replace("\", "/") + "$esc[33m >$esc[0m ";
}

Like bash, which loads .bashrc in an interactive session, PowerShell can be customized similarly 🗄️.

It is possible to change the prompt and define aliases and functions.

The prompt

The prompt is customized by defining a function named prompt.

In my case, I like to see the user name and the current directory (which is more o less the default setting in most GNU/Linux environments)

Notice that ANSI Escape Sequences are supported only from Windows 10 onwards (thank you WSL! 🗄️). To support older consoles, one needs to write something like

function prompt {
  Write-Host "$env:UserName" -ForegroundColor Green -NoNewline;
  Write-Host " $pwd".Replace(" $home", " ~").Replace("\", "/")  -ForegroundColor Blue -NoNewline;
  return " > ";
}

I do not like this variant for multiple reasons. First, notice how the last line is a return and not a Write-Host. Normally there would be no difference, but in this case, if a return is missing, Poweshells adds `\nPS> ` to the prompt.

Second, multiple calls to Write-Host are necessary for something that should be only one output. As Write-Host supports only one ForegroundColor for the whole output, it is not possible to merge them. In this case, it probably cannot happen, but what if someone else writes to the console between those calls?

Third, if invoking Powershell from another shell that is not Powershell, like bash, there would be no colorized output. Also, the vast majority of terminal emulators support those ANSI Escape Sequences, and they can be saved in files too.

As \ is used as an escape character in many programming languages (PowerShell excluded, as it uses `), all \ in the path in the prompt is replaced by /. To reduce the length of the prompt, the home directory of the current user is replaced by ~, just like in most GNU/Linux shells.

It is possible to add other information in the prompt, but so far I am happy with a more minimalistic approach. I would like to add the exit code/error of the last command (if it did not end successfully), but I did not find a reliable way to query it.

Keybindings

Set-PSReadLineOption -EditMode Emacs; is there to ensure I can use Ctrl+D to close my shells, like all other shells (except cmd), and even other interactive programs like gp, python, …​

It also sets other keybindings, like Ctrl+R for searching the history.

Following settings

Set-PSReadLineKeyHandler -Key Ctrl+Shift+C -Function Copy;
Set-PSReadLineKeyHandler -Key Ctrl+Shift+V -Function Paste;

Ensure that the combination Ctrl+⇧ Shift+C and Ctrl+⇧ Shift+V work for copy-pasting. It is the convention used in most terminal emulators in Posix-like environments, as Ctrl+C is used for terminating the current program.

For consistency, after opening a console, right-click on the title. Then View  Properties  Options  "Edit Options" and select "Use Ctrl+Shift+C/V as Copy/Paste"

It is otherwise possible to change this setting programmatically (for both powershell.exe and cmd.exe, both 32 and 64-bit versions) with

reg add "HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe" /v InterceptCopyPaste /t REG_DWORD /d 1 /f
reg add "HKEY_CURRENT_USER\Console\%SystemRoot%_SysWOW64_cmd.exe" /v InterceptCopyPaste /t REG_DWORD /d 1 /f
reg add "HKEY_CURRENT_USER\Console\%SystemRoot%_System32_WindowsPowerShell_v1.0_powershell.exe" /v InterceptCopyPaste /t REG_DWORD /d 1 /f
reg add "HKEY_CURRENT_USER\Console\%SystemRoot%_SysWOW64_WindowsPowerShell_v1.0_powershell.exe" /v InterceptCopyPaste /t REG_DWORD /d 1 /f

Note that those settings won’t change the behavior of other keybindings, like Ctrl+.

Aliases and functions

The rest of my profile file sets some aliases for commands I use very often, or for which I have similar aliases on my GNU/Linux machines. Even if the output and effects are not identical on Unix shells, they are mostly sufficient.

But defining a customized prompt and aliases and functions is not sufficient for removing some annoying behaviors described above.

Customize the terminal emulator

Those changes do not depend on PowerShell and can be applied to cmd too.

Colors

There seems to be an easy fix

After opening a console, right-click on the title. Then View  Properties  Color  Screen Background and at last select the desired color.

Note that Microsoft released Colortool 🗄️ for changing semi-automatically the color scheme.

Cursors

This setting is only available from Windows 10 (thank you WSL! 🗄️).

After opening a console, right-click on the title. Then View  Properties  Terminal  Cursor Shape  Solid Box

It is otherwise possible to change this setting programmatically (for both powershell.exe and cmd.exe, both 32 and 64-bit versions) with

reg add "HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe" /v Cursortype /t REG_DWORD /d 4 /f
reg add "HKEY_CURRENT_USER\Console\%SystemRoot%_SysWOW64_cmd.exe" /v Cursortype /t REG_DWORD /d 4 /f
reg add "HKEY_CURRENT_USER\Console\%SystemRoot%_System32_WindowsPowerShell_v1.0_powershell.exe" /v Cursortype /t REG_DWORD /d 4 /f
reg add "HKEY_CURRENT_USER\Console\%SystemRoot%_SysWOW64_WindowsPowerShell_v1.0_powershell.exe" /v Cursortype /t REG_DWORD /d 4 /f

Scroll Forward

This setting is only available from Windows 10 (thank you WSL! 🗄️).

After opening a console, right-click on the title. Then View  Properties  Terminal  Terminal Scrolling  Disable Scroll-Forward

It is otherwise possible to change this setting programmatically (for both powershell.exe and cmd.exe, both 32 and 64-bit versions) with

reg add "HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe" /v TerminalScrolling /t REG_DWORD /d 1 /f
reg add "HKEY_CURRENT_USER\Console\%SystemRoot%_SysWOW64_cmd.exe" /v TerminalScrolling /t REG_DWORD /d 1 /f
reg add "HKEY_CURRENT_USER\Console\%SystemRoot%_System32_WindowsPowerShell_v1.0_powershell.exe" /v TerminalScrolling /t REG_DWORD /d 1 /f
reg add "HKEY_CURRENT_USER\Console\%SystemRoot%_SysWOW64_WindowsPowerShell_v1.0_powershell.exe" /v TerminalScrolling /t REG_DWORD /d 1 /f

Cursor blinking

There does not seem to be a console-specific setting. But there is a system-wide setting, which is thus even better, as it is respected by other terminal emulators and programs, like mintty and Firefox.

View  Control Panel  Keyboard  Cursor Blink rate and move the slider to "None".

Note 📝
This setting has been there at least since Windows XP.

It is possible to change this setting programmatically with

reg add "HKEY_CURRENT_USER\Control Panel\Desktop" /v CursorBlinkRate /t REG_SZ /d "-1" /f
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Try the new cross-platform PowerShell https://aka.ms/pscore6

Depending on the version, you might not see an advertisement for another version of PowerShell to download. Also, the advertisement I get is outdated. The current PowerShell version that can be downloaded is 7.2, and currently, the link redirects to https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2. Thus it seems not to be possible to install PowerShell Core 6.0 🗄️ with the given link.

Currently, there is no satisfying solution.

There is no setting in PowerShell itself to change this behavior for all instances.

psh.lnk

A possible workaround is to create an appropriate shortcut psh.lnk and use it for launching the shell. From ⊞ Win+R it is possible to type psh, this would execute powershell -NoLogo and not display the logo, as desired.

The first "disadvantage" is that it needs to be named differently. If named powershell.lnk, ⊞ Win+R would pick powershell.exe, unless one writes powershell.lnk instead of powershell, which would not be user-friendly.

The second disadvantage is that not all programs do not accept a .lnk file as an executable. PowerShell and cmd.exe both do not accept it, as well as terminal emulators like mintty, or the nvim editor.

psh.cmd

A file like

psh.cmd
@echo off
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoLogo %*

is recognized as an executable from most programs.

Just like psh.lnk it cannot be named powershell.cmd (or powershell.bat), otherwise one needs to specify the filename extension from ⊞ Win+R and other contexts.

This approach has the disadvantage that the terminal emulator window has the wrong icon (it shows the cmd icon) and the wrong title (it has C:\WINDOWS\system32\cmd.exe as the title).

It also seems to cause some issues with key shortcuts when invoked from Cygwin.

Note 📝
It is possible to add both psh.cmd and psh.lnk.

Program-specific settings

With third-party terminal emulators, it is often possible to define an appropriate command that executes PowerShell with the parameter we want:

mintty.exe --title PowerShell --icon C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe --exec powershell -NoLogo

With Microsoft Terminal, there is a configuration file where one can customize how different shells are invoked.

However, not all programs that invoke a shell might have such flexibility.

And even if all programs would be that flexible, the main disadvantage is that one needs to configure them all one by one. If PowerShell is invoked from something else, or if it is executed with ⊞ Win+R, one would still not get the desired behavior.

Clear screen

Probably the only approach that currently works on all instances is executing clear when the sell is started, by adding the command to the profile file.

This does much more than simply hiding the logo, as it clears everything that has been written to the console before the clear command, albeit normally there are no other outputs.

Startup time

Sometimes starting PowerShell takes a lot of time.

The startup time can be roughly measured with powershell -noprofile -ExecutionPolicy Bypass ( Measure-Command { powershell "Write-Host 1" } ).TotalSeconds, and sometimes, it is bigger than 5 seconds (!).

A common approach is to compile ngen.exe

$env:path = [Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory();
[AppDomain]::CurrentDomain.GetAssemblies() | % {
  if (! $_.location) {continue}
  $Name = Split-Path $_.location -leaf;
  Write-Host -ForegroundColor Yellow "NGENing : $Name";
  ngen install $_.location | % {"`t$_"};
}

Otherwise, exclude it from Windows Defender

Add-MpPreference -ExclusionProcess powershell

Or remove some cache files

Remove-Item -Recurse -Force $env:APPDATA/Microsoft/Windows/PowerShell/

Do you want to share your opinion? Or is there an error, some parts that are not clear enough?

You can contact me anytime.