Skip to content

Instantly share code, notes, and snippets.

@chrisfcarroll
Last active February 26, 2025 11:16
Show Gist options
  • Save chrisfcarroll/f3ecb2892f996149ee039d48abb57101 to your computer and use it in GitHub Desktop.
Save chrisfcarroll/f3ecb2892f996149ee039d48abb57101 to your computer and use it in GitHub Desktop.
Common Aliases and Paths for PowerShell Profile Microsoft.PowerShell_profile.ps1: editors, paths, git, DevAzure, docker, dotNet, MsBuild, NuGet, IIS, p4merge, poshgit
# https://gist.github.com/chrisfcarroll/f3ecb2892f996149ee039d48abb57101
# Aliases and Paths for PowerShell Profile ~\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
#
# ----------------------------------------------------
# For machines where you have no privileges you should be able to enable scripts for yourself only with:
#
# `Set-ExecutionPolicy RemoteSigned -Scope CurrentUser`
# ----------------------------------------------------
#
# Sections: Paths, Editors, Tab Completion, Git abbreviations, Docker abbreviations, other prefs, firstRun and windows installers
#
Param([switch]$firstRun)
# ============================================================================
# -- Paths
# Separate secrets from publicly shareable generic profile setup
if(test-path ~/.secrets.ps1){ . ~/.secrets.ps1 ; function editsecrets {edit (Resolve-Path ~/.secrets.ps1)} }
# Separate current-project specific setup from publically shareable generic profile setup
if(test-path ~/abbreviations.ps1){ . ~/abbreviations.ps1 ; function editabbreviations {edit (Resolve-Path ~/abbreviations.ps1)} }
# ============================================================================
# My Working Directories
# Edit this list to match your usage.
# Left hand side is a directory path, right hand side is a commandline alias to use for cd to that path
$workDirShortcuts= @{
"~/Repos"="cdr"
"~/Deploy"="cdd"
"~/Scratch"="cdsu"
"~/Scratch-Trusted"="cds"
}
$notOkay= @{}
foreach($sh in $workDirShortcuts.GetEnumerator()){
$myError=$null
$globalVarName = (Split-Path $sh.Name -Leaf) -replace "[^a-zA-Z0-9_]",""
iex "`$global:$globalVarName = (Resolve-Path $($sh.Name))" -ErrorVariable myError 2>&1 >$null
if($myError){
$notOkay.Add( $sh.Name , $sh.Value )
}else{
iex "function $($sh.Value) { Set-Location `$global:$globalVarName}"
}
}
if($notOkay.Count){
Write-Host "Your `$PROFILE script lists these working directory shortcuts which don't exist. Do edit your shortcut list."
$notOkay | Out-Host
}
Remove-Variable workDirShortcuts,notOkay,myError,globalVarName
function Add-ToPath( $directoryOrFile, [switch]$applyToWindowsUserPath ){
if(-not $directoryOrFile -gt ""){return}
if(-not (Test-Path $directoryOrFile)){return}
if((Test-Path $directoryOrFile -PathType Leaf)){$directoryOrFile = Split-Path $directoryOrFile -Parent }
$directoryOrFile = (Resolve-Path $directoryOrFile).Path
if($env:Path.ToLower().Contains( $directoryOrFile.ToLower()) ) { return }
if($applyToWindowsUserPath.IsPresent){
Write-Warning "Adding $directoryOrFile permanently to your User PATH"
$userPath=[Environment]::GetEnvironmentVariable("Path","User").Trim(';')
[Environment]::SetEnvironmentVariable("Path", "$userPath;$directoryOrFile", 'User')
}else{
$env:Path="$($env:PATH.Trim(';'));$directoryOrFile"
}
}
# ============================================================================
# -- Editors
# set the edit to command to whichever of sublime, vscode-insiders, vscode, vim we can find, in that order of preference.
if(Test-Path "C:\Program Files\Vim\vim90\vim.exe"){$global:EDITOR="C:\Program Files\Vim\vim90\vim.exe"}
if(Get-Command code -ea SilentlyContinue)
{
New-Alias edit code
}
elseif (Get-Command code-insiders -ea SilentlyContinue)
{
new-alias code code-insiders
New-Alias edit code-insiders
}
elseif(Get-Command subl -ea SilentlyContinue)
{
New-Alias edit subl
}
elseif(Test-Path ~/Applications/sublime_text_build_4126_x64/subl.exe)
{
function subl{ ~/Applications/sublime_text_build_4126_x64/subl.exe $args }
New-Alias edit subl
}
function editprofile { edit $PROFILE }
if((Test-Path "C:\Program Files\Vim\vim90\vim.exe") -and -not (Get-Command vim -ea SilentlyContinue))
{
New-Alias vim "C:\Program Files\Vim\vim90\vim.exe"
New-Alias vi "C:\Program Files\Vim\vim90\vim.exe"
}
# ============================================================================
# Autocompletion and readline vvv
# This now seems to be redundant on newer machines, it's all built-in even for PS5?
# Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete
# Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward
# Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward
# Autocompletion ^^^
# ============================================================================
# -- Git
Write-Host "Use source command '. pg' to load poshgit"
# I used to have poshgit load automatically but it's slow - often 2-3 seconds -
# so autoload seems unhelpful if you use new shells a lot
function pg {
$stopwatchPG=[System.Diagnostics.Stopwatch]::StartNew()
Import-Module posh-git -ErrorAction SilentlyContinue
Write-Host "PoshGit took $($stopwatchPG.ElapsedMilliseconds)ms"
if($?){
$GitPromptSettings.DefaultPromptAbbreviateHomeDirectory = $true
function Get-ShorterPromptPath {
(Get-Location).ToString().Replace("$env:USERPROFILE","~") `
-replace '([^\\]{6})([^\\]{4,})\\','$1..\' `
-replace '\\([^\\]{6})([^\\]{3,})([^\\]{6})$','\$1..$3'
}
$GitPromptSettings.DefaultPromptPath.Text = '$(Get-ShorterPromptPath)'
function nll { if(-not $GitPromptSettings.AfterStatus.Text -match "\n"){$GitPromptSettings.AfterStatus.Text += "`n" }}
}else{
function prompt {
$d=(Get-Location).ToString().Replace("$env:USERPROFILE","~") `
-replace '([^\\]{6})([^\\]{4,})\\','$1..\' `
-replace '\\([^\\]{6})([^\\]{3,})([^\\]{6})$','\$1..$3'
"$d> "
}
}
}
function gaa { git add $args :/ }
function gb { git branch $args }
function gbr { git branch --remote $args }
function gcliplast { (git show --pretty=oneline | head -n 1) -replace "^[a-f0-9A-F]+ ","" | clip }
function gco { git checkout $args}
function gitcom { git commit -m "$args"}
function gitacom { git commit -a -m "$args"}
function gitaaacom { git add :/ ; git commit -m "$args"}
function gdiff { git diff $args }
function glog { git log `
--pretty=format:'%Cred%h%Creset %C(yellow)%d%Creset %s %Cgreen%cd %C(bold blue)%an%Creset' `
--abbrev-commit --date=format:'%d-%m-%Y %H:%M' $args }
function glogg { glog --graph $args }
function glog1 {git log --pretty=oneline $args}
function grv {get remote --verbose $args}
function gs { git status $args }
function gpullpush {
"Checking ..."
git remote update
if( ($s=(git status | select-string -Pattern "Your branch is behind|have diverged")).Count ) {
$s
git pull
if( $s -match "can be fast-forwarded.|Successfully rebased and updated"){
"Pushing ..."
git push
} else {
"Merge or rebase wanted. Not pushing."
}
} else {
"Nothing to pull. Pushing ..."
git push
}
Get-Date -DisplayHint DateTime
}
function openremote { git remote -v | select-string "https://[^ ]+" | Select-Object -First 1 | %{ Start-Process ("$($_.Matches.Value)" -replace "//[^@]+@","//") } }
#Git ^^^
# ============================================================================
# -- DevAzure
function Open-DevAzure([string][ValidateSet("","build","release","boards","repo","files","pullrequests","branches","tags","commits")]$what) {
$remote = "$(git remote -v | select-string "https://[^ ]+" | Select-Object -First 1 -Property {$_.Matches.Value} | Select-Object -ExpandProperty '$_.Matches.Value')" -replace "//[^@]+@","//"
if("build","release","boards" -contains $what)
{
$remote | select-string "https://[^ ]+(?=/_git)" | Select-Object -First 1 | %{ Start-Process "$($_.Matches.Value)/_$what" }
}
elseif( "files","repo","" -contains $what)
{
$what=""
$remote | select-string "https://[^ ]+" | Select-Object -First 1 | %{ Start-Process "$($_.Matches.Value)" }
}
else
{
$remote | select-string "https://[^ ]+" | Select-Object -First 1 | %{ Start-Process "$($_.Matches.Value)/$what" }
}
}
new-alias opendaz Open-DevAzure
#DevAzure ^^^
# ============================================================================
# -- Docker
if(Get-Command docker -ErrorAction SilentlyContinue){
function dockerdesktop { Start-Process "C:\Program Files\Docker\Docker\Docker Desktop.exe" }
function ddls-all { docker container ls -aq }
function ddstop-all { docker container stop $(docker container ls -aq) }
function ddrm-all { docker container rm $(docker container ls -aq) }
}
#Docker ^^^
# ============================================================================
# -- AzureCLI
if(Get-Command az -ErrorAction SilentlyContinue){
Register-ArgumentCompleter -Native -CommandName az -ScriptBlock {
param($commandName, $wordToComplete, $cursorPosition)
$completion_file = New-TemporaryFile
$env:ARGCOMPLETE_USE_TEMPFILES = 1
$env:_ARGCOMPLETE_STDOUT_FILENAME = $completion_file
$env:COMP_LINE = $wordToComplete
$env:COMP_POINT = $cursorPosition
$env:_ARGCOMPLETE = 1
$env:_ARGCOMPLETE_SUPPRESS_SPACE = 0
$env:_ARGCOMPLETE_IFS = "`n"
$env:_ARGCOMPLETE_SHELL = 'powershell'
az 2>&1 | Out-Null
Get-Content $completion_file | Sort-Object | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, "ParameterValue", $_)
}
Remove-Item $completion_file, Env:\_ARGCOMPLETE_STDOUT_FILENAME, Env:\ARGCOMPLETE_USE_TEMPFILES, Env:\COMP_LINE, Env:\COMP_POINT, Env:\_ARGCOMPLETE, Env:\_ARGCOMPLETE_SUPPRESS_SPACE, Env:\_ARGCOMPLETE_IFS, Env:\_ARGCOMPLETE_SHELL
}
}
#AzureCLI ^^^
# ============================================================================
# -- Dotnet CLI
Add-toPath ~/.dotnet
Add-ToPath ~/.dotnet/tools
#DotNet ^^^
# ============================================================================
# -- Console Colours for the PowerShell White or Yellow on Blue theme
$Host.PrivateData.ErrorForegroundColor = "Yellow"
$Host.PrivateData.ErrorBackgroundColor = $Host.UI.RawUI.BackgroundColor
$Host.PrivateData.WarningForegroundColor = "DarkYellow"
$Host.PrivateData.WarningBackgroundColor = $Host.UI.RawUI.BackgroundColor
#Console Colours ^^^
# ============================================================================
# -- Other preferences
new-alias gh get-help
new-alias ll Get-ChildItem
new-alias open start
function ss { start-process "https://bing.com/search?q=$([uri]::EscapeDataString($args))" }
function pss { start-process "https://bing.com/search?q=$([uri]::EscapeDataString("powershell $args"))" }
# which.exe,where.exe,get-command are each subtly different
function wh { $r= Get-Command $args ; $r; if($r.GetType() -eq [System.Management.Automation.FunctionInfo]){ $r.ScriptBlock } }
if([System.Environment]::OSVersion.Platform -like 'Win*')
{
if(Test-Path "$global:onedrive\Commands\AutohotkeyForPCKeyboard.exe"){
new-alias ahwin (Resolve-Path "$global:onedrive\Commands\AutohotkeyForPCKeyboard.exe")
}
if(Test-Path "$global:onedrive\Commands\AutohotkeyForMacKeyboard.exe"){
new-alias ahmac (Resolve-Path "$global:onedrive\Commands\AutohotkeyForMacKeyboard.exe")
}
function pgrep([Parameter(Position=0,mandatory=$true)][string]$Name, [switch]$full )
{
Get-WmiObject Win32_Process `
| Where-Object { $_.Name -match $Name -or ( $full -and ($_.CommandLine -match $Name)) } `
| Select-Object -Property ProcessId, Name, CommandLine, Status
}
}
Add-Type -AssemblyName System.IO.Compression.FileSystem
function Get-ZippedChildItem {
param (
[Parameter(Mandatory=$false)]
[string]$Path,
[Parameter(Mandatory=$false)]
[string]$filePattern
)
Add-Type -AssemblyName System.IO.Compression.FileSystem
if(-not $Path){
'Usage:
Get-ZippedChildItem [ -Path ] "archive.zip" [ -FilePattern "file*.* ]"
'
return
}
if (-not $Path) {$Path="."}
if (-not $ZipPattern) {$ZipPattern="*.zip"}
if (-not $filePattern) {$filePattern="*"}
Get-ChildItem -Path $Path | ForEach-Object {
$zipFile = $_.FullName
$zipArchive = [System.IO.Compression.ZipFile]::OpenRead($zipFile)
try{
$zipArchive.Entries | Where-Object { $_.Name -like $filePattern }
} finally { $zipArchive.Dispose() }
}
}
function Get-ZippedContent{
[OutputType([String])]
[CmdletBinding()]
Param( $ZipFilePath, $FilePathInZip)
$verbose = $VerbosePreference -ne 'SilentlyContinue'
if (-not (test-path $ZipFilePath)) {
throw "Zip file ""$ZipFilePath"" not found."
}
try {
$Zip = [System.IO.Compression.ZipFile]::OpenRead( (Resolve-Path $ZipFilePath) )
write-verbose "Opened $ZipFilePath : $(($zz.Entries | measure).Count) entries"
$matchingEntries= $Zip.Entries | where-object {return $_.FullName -like $FilePathInZip}
write-verbose "$(($matchingEntries | measure).Count) entries match ""$FilePathInZip"""
$matchingEntries |
ForEach-object {
write-verbose "Found $($_.FullName)"
$ZipStream = $_.Open()
try{
$Reader = [System.IO.StreamReader]::new($ZipStream)
$s= $Reader.ReadToEnd()
write-verbose "Length $($s.Length)"
$s
}
finally{
if($Reader){$Reader.Dispose()}
}
}
}
finally {if ($Zip) { $Zip.Dispose() } }
}
function yesno {
while( -not ( ($choice= (Read-Host "May I continue?")) -match "^(ye?s?|no?)$")){ "Y or N ?"}
return ($choice -match "^y")
}
#Other preferences ^^^
# ============================================================================
# -- DotNet Installs
class PSChildNameVersionRelease { [string]$PSChildName ; [string]$Version ; [int]$Release; }
function Get-InstalledNetFrameworkRuntimes{
[OutputType([PSChildNameVersionRelease])]
[CmdletBinding()]
Param()
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse |
Get-ItemProperty -name Version,Release -EA 0 |
Where-Object { $_.PSChildName -match '^(?!S)\p{L}'} |
Sort-Object -Property Version |
Select-Object PSChildName, Version, Release
}
class DisplayNameVersion { [string]$DisplayName ; [string]$DisplayVersion ; }
function Get-InstalledDotNetPacks{
[OutputType([DisplayNameVersion])]
[CmdletBinding()]
Param()
Get-ItemProperty `
-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", `
"HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" |
Where-Object {$_.DisplayName -like '*.NET*'} |
Select-Object DisplayName, DisplayVersion |
Sort-Object -Property DisplayName
}
#DotNet Installs ^^^
# ============================================================================
# -- IIS
function publish-to-iis($fromSolutionDirectory, $toIISAppPath, $publishProfileXmlFileNameNoPath, $buildConfiguration, [switch]$noConfirm)
{
if(-not "$fromSolutionDirectory" -or -not "$toIISAppPath" `
-or -not "$publishProfileXmlFileNameNoPath" -or -not "$buildConfiguration"){
Write-Host "Usage: publish-to-iis `$fromSolutionDirectory, `$toIISAppPath, `$publishProfileXmlFileNameNoPath, $buildConfiguration"
break
}
Get-ChildItem $fromSolutionDirectory/$publishProfileXmlFileNameNoPath -recurse
Push-Location $fromSolutionDirectory
if(Test-Path $toIISAppPath\web.config){
Remove-Item $toIISAppPath\web.config -Verbose
}else{
Write-Warning "target web.config doesn't exist. Did you already delete it (or have you set the WRONG target path)?"
if(-not $noConfirm){
while( -not ( ($choice= (Read-Host "Press Y to continue or N to stop:")) -match "^(Y|N)$")){ "Y or N ?"}
if($choice -ne 'y'){return}
}
}
$publishProfileXmlFileNameNoPath= Split-Path $publishProfileXmlFileNameNoPath -Leaf
"Removing directories..."
Get-ChildItem $toIISAppPath\* -Directory |
ForEach-Object{ $_.Name ; Remove-Item $_ -recurse }
Get-ChildItem $toIISAppPath
msbuild /p:Configuration=$buildConfiguration /p:DeployOnBuild=true /p:PublishProfile=$publishProfileXmlFileNameNoPath
Pop-Location
"Finished at $(Get-Date)"
}
#IIS ^^^
# ============================================================================
# -- First Run and Installers ------------------------------------------------
if($firstRunGit)
{
if(Get-Command git -ErrorAction SilentlyContinue - $?){
winget install git.git
}
git config --global alias.root 'rev-parse --show-toplevel'
git config --global alias.lg "log --color --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --graph"
git config --global alias.lg1 "log --color --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
if((Get-Command p4merge) -and (Test-Path (Get-Command p4merge).Source))
{
git config --global merge.tool p4merge
git config --global diff.tool p4merge
}
else{Write-Warning "p4merge not found"}
Install-Module -Name posh-git -Scope CurrentUser
}
if($firstRunDotNet){
winget install Microsoft.NuGet
winget install Microsoft.DotNet.Framework.DeveloperPack_4
}
function Show-MoreWinGetInstallersForDevTools{
@"
More WinGet DevTool installers
==============================
winget install WinMerge.WinMerge
winget install Microsoft.VisualStudioCode --override "/mergetasks='!runcode,addcontextmenufiles,addcontextmenufolders,associatewithfiles,addtopath'" --source winget
winget install Microsoft.VisualStudio.2022.Enterprise
winget install Microsoft.AzureCLI
winget install Microsoft.DotNet.Framework.DeveloperPack_4
winget install autohotkey.autohotkey
"@
}
# ============================================================================
# -- Windows only Visual Studio
function Ensure-MsBuild-Git-Nuget-If-WinVS-Installed
{
if( [System.Environment]::OSVersion.Platform -notlike "Win*" ){
Write-Error "This function relies on a Visual Studio installation so it only runs on Windows."
return
}
$vswherePath= "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer"
if(-not (Test-Path $vswherePath)){
Write-Error "This function relies on a Visual Studio installation but didn't find one in ."
Write-Warning "You can install visual studio and the VSWhere tool with winget, e.g.:
winget search Microsoft.VisualStudio
# winget install <Your Choice of lastest Visual Studio version e.g. Microsoft.VisualStudio.2022.Enterprise/Professional/Community>
winget install Microsoft.VisualStudio.2022.Community
winget install Microsoft.VisualStudio.Locator
"
return
}
Write-Host @"
This script will
- add Visual Studio MsBuild to your path
- add Git to your path
- Install NuGet
- Install NuGet artifacts credential provider
"@
Add-ToPath $vswherePath
$findVSComponents = @{
"MSBuild" = @{ Path = "MSBuild\**\Bin\MSBuild.exe" }
"Git" = @{ Path = "**\Cmd\Git.exe" }
# For more uptodate Git, use winget install git.git instead of using the VS installed version
}
foreach($required in $findVSComponents.GetEnumerator()){
if(Get-command $required.Key -ErrorAction SilentlyContinue){
Write-Host "$($required.Key) already in path"
continue
}
$componentExe = &vswhere.exe -latest -prerelease -products * -find $required.Value.Path
Add-ToPath "$componentExe" -applyToWindowsUserPath
if(-not (Get-Command $required.Key -ErrorAction SilentlyContinue)){
Write-Warning "Failed to add $($required.Key) to the path at $componentExe"
}else{
"Added $($required.Key) to the path at $componentExe"
}
}
if(-not (Get-Command Git -ErrorAction SilentlyContinue)){
winget install Git.Git
Write-Warning "Just installed Git. You could exit and restart powershell for the addition to your path to be picked up."
}
if(-not (Get-Command NuGet -ErrorAction SilentlyContinue)){
winget install Microsoft.NuGet
Write-Warning "Just installed NuGet. You must exit and restart powershell for the addition to your path to be picked up."
}
Write-Warning "Installing the azure artifacts credential provider for netcore and NetFx."
iex "& { $(irm https://aka.ms/install-artifacts-credprovider.ps1) } -AddNetfx"
}
# Windows only Visual Studio ^^^
Remove-Variable firstRun
#First Run ^^^
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment