Customize powershell
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
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
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
and at last select the desired color.Cursors
This setting is only available from Windows 10 (thank you WSL! 🗄️).
After opening a console, right-click on the title. Then
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
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.
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
Logo
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
@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.