Over the course of the last several months, I’ve collected several snippets for my PowerShell profile. Your PowerShell profile is loaded each time you start PowerShell. The official documentation for it can be found here: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-6
To get started, you can easily edit your profile by typing notepad $profile
to load your profile. The PowerShell prompt and PowerShell ISE each have their own profile but I like mine to be the same so here’s what I did:
- First, I created a profile.ps1 file in
$home\Documents\WindowsPowerShell\
. This is also where you’ll find the default profile files, Microsoft.PowerShell_profile.ps1 and Microsoft.PowerShellISE_profile.ps1. -
Second, in the default files, just add a line to redirect the profile like so:
1
$profile="$home\Documents\WindowsPowerShell\profile.ps1"
- Third, save all 3 files. Next time you open PowerShell or the ISE, it will load your profile.ps1 (which might be empty at this point).
Let’s walk through what’s in my profile.ps1. At the end, I’ll include a link so you can download it in its entirety.
Logging and PSReadLine
Here I’m setting my log path and will log everything I type so I have a full transcript of my session if ever I need it. Next, some PSReadLine settings. @roggenk has a good blog post that covers PSReadLine in some detail so head over and check that out. I’ve got some of the same settings here that improve the user experience. I have two custom key bindings, one to start the @vivaldibrowser browser (my favorite browser!) and switches focus to the browser right away and the other to perform a git commit/push.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #Logging #Logging $PSLogPath = ("{0}{1}\Documents\WindowsPowerShell\log\{2:yyyyMMdd}-{3}.log" -f $env:HOMEDRIVE, $env:HOMEPATH, (Get-Date), $PID) Add-Content -Value "# $(Get-Date) $env:username $env:computername" -Path $PSLogPath -erroraction SilentlyContinue Add-Content -Value "# $(Get-Location)" -Path $PSLogPath -erroraction SilentlyContinue # PSReadLine Settings Set-PSReadLineOption -HistorySearchCursorMovesToEnd Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward Set-PSReadlineOption -BellStyle None #Disable ding on typing error Set-PSReadlineOption -EditMode Emacs #Make TAB key show parameter options Set-PSReadlineKeyHandler -Key Ctrl+i -ScriptBlock { Start-Process "${env:ProgramFiles(x86)}\vivaldi\Application\vivaldi.exe" -ArgumentList "https://www.bing.com" } #KEY: Load Browsers using key "C:\Program Files (x86)\Vivaldi\Application\vivaldi.exe" #KEY: Git, press Ctrl+Shift+G (case sensitive) Set-PSReadlineKeyHandler -Chord Ctrl+G -ScriptBlock { $message = Read-Host "Please enter a commit message" /usr/bin/git commit -m "$message" | Write-Host $branch = (/usr/bin/git rev-parse --abbrev-ref HEAD) Write-Host "Pushing ${branch} to remote" /usr/bin/git push origin $branch | Write-Host } |
Functions
Next step, some functions. The first one just shows some nice output to get the current directory size, which should be built-in! The next one checks to see if the PowerShell prompt is running as an administrator; you’ll see why we need this later. I stole borrowed both of these from @jaredtrog including most of the other stuff in my prompt below (next section).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #Functions function Get-DirectorySize($Path='.',$InType="MB") { $colItems = (Get-ChildItem $Path -recurse | Measure-Object -property length -sum) switch ($InType) { "GB" { $ret = "{0:N2}" -f ($colItems.sum / 1GB) + " GB" } "MB" { $ret = "{0:N2}" -f ($colItems.sum / 1MB) + " MB" } "KB" { $ret = "{0:N2}" -f ($colItems.sum / 1KB) + " KB"} "B" { $ret = "{0:N2}" -f ($colItems.sum) + " B"} Default { $ret = "{0:N2}" -f ($colItems.sum) + " B" } } Return $ret } function Test-IsAdmin { ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") } |
The Prompt
Finally, here’s my prompt. You’ll see the Test-IsAdmin function being used here to set the prompt’s text color. This is really useful to know immediately if you’re running an elevated prompt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | function global:prompt { #Put the full path in the title bar $console = $host.ui.RawUI $console.ForegroundColor = "gray" $host.UI.RawUI.WindowTitle = Get-Location #Set text color based on admin if (Test-IsAdmin) { $userColor = 'Red' } else { $userColor = 'White' } #Setup command line numbers $LastCmd = Get-History -Count 1 if($LastCmd) { $lastId = $LastCmd.Id Add-Content -Value "# $($LastCmd.StartExecutionTime)" -Path $PSLogPath Add-Content -Value "$($LastCmd.CommandLine)" -Path $PSLogPath Add-Content -Value "" -Path $PSLogPath } $nextCommand = $lastId + 1 $fullPath = Get-Location Write-Host "[$($pwd)]" -ForegroundColor "Cyan" Write-Host -NoNewline '[' -ForegroundColor "Gray" Write-Host -NoNewline "$([System.Environment]::UserName)" -ForegroundColor $userColor Write-Host -NoNewline '@' Write-Host -NoNewline "$([System.Environment]::MachineName) $nextCommand" -ForegroundColor $userColor Write-Host -NoNewline ']' -ForegroundColor "Gray" #Use $host.EnterNestedPrompt() to test a nested prompt. Write-Host -NoNewline " PS$('>' * ($nestedPromptLevel + 1)) " -ForegroundColor $userColor Return " " } |
Here’s how it looks:
Having the folder path on its own line gives me more space to focus on my account PowerShell command. Here’s how the Administrator prompt looks:
Notice the red text. Almost forgot the nice ASCII. Just use any online ASCII generator and use Write-Host.
1 2 3 4 5 6 7 | Clear-Host Write-Host ' _ __ __ _ __' Write-Host ' | | / /___ _/ /_ (_)___/ /' Write-Host ' | | /| / / __ `/ __ \/ / __ / ' Write-Host ' | |/ |/ / /_/ / / / / / /_/ / ' Write-Host ' |__/|__/\__,_/_/ /_/_/\__,_/ ' Write-Host ' ' |
That’s all! If you have any other tips, share them in the comments below.