Customize powershell


9 - 11 minutes read, 2189 words
Categories: shell windows
Keywords: WSL cygwin default editor powershell shell ssh vim windows

In a previous article I already wrote that I change 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 ssh shell. In the worst-case scenario, it is 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. Here 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 settings, 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(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!). For supporting 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 together. 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 really like to add the exit code 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 event other interactive programs like gp.

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+Space bar.

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 function 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 lest 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 (again, 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 (again, 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. So It is apparently not 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 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 really user-friendly.

The second disadvantage is that most programs do not accept a .lnk file as executable. PowerShell and cmd.exe both do not accept it, as also 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 environments.

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’s C:\WINDOWS\system32\cmd.exe).

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 the Microsoft Terminal, there is a configuration file where one can customize how different shells are invoked.

But 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 all tools he uses, directly or indirectly. 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 prior to 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 here.