# make windows feel more like zsh

> Source: <https://gist.github.com/benjar12sc/92cf6eb83303a17bdcb28c1d5f3965b8>
> Published: 2026-06-08 19:47:11+00:00

| # ============================================================================== | |
| # 1. Shell Environment & Initialization | |
| # ============================================================================== | |
| # Eject the default, outdated PSReadLine module from memory | |
| Remove-Module PSReadLine -ErrorAction SilentlyContinue | |
| # Import the newly installed version | |
| Import-Module PSReadLine | |
| # PSReadLine Configurations | |
| Set-PSReadLineOption -PredictionSource History | |
| Set-PSReadLineOption -PredictionViewStyle ListView | |
| # Starship Prompt (Renders the Unix look and feel) | |
| if (Get-Command starship -ErrorAction SilentlyContinue) { | |
| Invoke-Expression (&starship init powershell) | |
| } | |
| # Zoxide (Smart directory jumper) | |
| if (Get-Command zoxide -ErrorAction SilentlyContinue) { | |
| Invoke-Expression (& { (zoxide init powershell | Out-String) }) | |
| } | |
| # ============================================================================== | |
| # 2. Core Unix Utilities & Environment Shims | |
| # ============================================================================== | |
| # Quick Directory Navigation | |
| function .. { Set-Location .. } | |
| function ... { Set-Location ../.. } | |
| function .... { Set-Location ../../.. } | |
| # 'la' and 'll' implementations (Handles hidden files and long lists) | |
| function la { | |
| if (Get-Command eza -ErrorAction SilentlyContinue) { | |
| eza -a @args | |
| } else { | |
| Get-ChildItem -Path $PWD.ProviderPath -Force @args | |
| } | |
| } | |
| function ll { | |
| if (Get-Command eza -ErrorAction SilentlyContinue) { | |
| eza -la @args | |
| } else { | |
| Get-ChildItem -Path $PWD.ProviderPath -Force @args | Format-Table Attributes, Name, @{Name="Size(KB)";Expression={[math]::round($_.Length / 1KB, 2)}}, LastWriteTime | |
| } | |
| } | |
| # 'which' implementation (Returns exact path of executable or function) | |
| function which ($command) { | |
| $cmd = Get-Command $command -ErrorAction SilentlyContinue | |
| if ($cmd) { | |
| $cmd | Select-Object -ExpandProperty Source | |
| } else { | |
| Write-Error "Command not found: $command" | |
| } | |
| } | |
| # 'touch' implementation (Create empty file or update timestamp) | |
| function touch ($file) { | |
| if (Test-Path $file) { | |
| (Get-Item $file).LastWriteTime = Get-Date | |
| } else { | |
| New-Item -ItemType File -Path $file -Force | Out-Null | |
| } | |
| } | |
| # 'ln' implementation (Handles standard symbolic links) | |
| # Usage: ln -s target_path link_path | |
| function ln { | |
| param([string]$arg1, [string]$arg2, [string]$arg3) | |
| if ($arg1 -eq "-s") { | |
| New-Item -ItemType SymbolicLink -Path $arg3 -Value $arg2 | |
| } else { | |
| New-Item -ItemType HardLink -Path $arg2 -Value $arg1 | |
| } | |
| } | |
| # Bash-style 'alias' function wrapper | |
| # Usage: alias ll="ls -la" or alias mycmd="git status" | |
| function alias { | |
| param([string]$expression) | |
| if (-not $expression -or -not ($expression -contains "=")) { | |
| Get-Alias | Select-Object Name, Definition | |
| return | |
| } | |
| $name, $definition = $expression -split '=', 2 | |
| $name = $name.Trim() | |
| $definition = $definition.Trim().Trim('"').Trim("'") | |
| if (-not ($definition -match '\s')) { | |
| Set-Alias -Name $name -Value $definition -Scope Global -Force | |
| } else { | |
| ScriptBlock::Create("function Global:$name { $definition `$args }") | Invoke-Command | |
| } | |
| } | |
| # 'export' implementation (Sets environment variables for the current session) | |
| # Usage: export MY_VAR="value" | |
| function export { | |
| param([string]$expression) | |
| if ($expression -match '=') { | |
| $name, $value = $expression -split '=', 2 | |
| $value = $value.Trim('"').Trim("'") | |
| [System.Environment]::SetEnvironmentVariable($name, $value, "Process") | |
| } | |
| } | |
| # 'ps' implementation (Supports 'aux' syntax and converts objects to searchable text lines) | |
| # Usage: ps aux | grep node | |
| function ps { | |
| param([string]$flags) | |
| if ($flags -match 'a|u|x') { | |
| Get-Process | ForEach-Object { | |
| [PSCustomObject]@{ | |
| PID = $_.Id | |
| Name = $_.ProcessName | |
| CPU = [math]::Round($_.CPU, 2) | |
| WS_MB = [math]::Round($_.WorkingSet / 1MB, 2) | |
| Id = $_.Id | |
| } | |
| } | Out-String -Stream | |
| } else { | |
| Get-Process @args | |
| } | |
| } | |
| # 'pkill' implementation (Kill processes easily by application name) | |
| # Usage: pkill node | |
| function pkill ($name) { | |
| Stop-Process -Name $name -Force -ErrorAction SilentlyContinue | |
| } | |
| # Clear Terminal Shorthand | |
| Set-Alias -Name c -Value Clear-Host | |
| # ============================================================================== | |
| # 3. Modern CLI Upgrades (Dynamic Mappings with Force Teardown) | |
| # ============================================================================== | |
| # Forcefully remove built-in locked aliases so our modern binaries can take over | |
| if (Get-Alias cat -ErrorAction SilentlyContinue) { Remove-Item alias:\cat -Force -ErrorAction SilentlyContinue } | |
| if (Get-Alias ls -ErrorAction SilentlyContinue) { Remove-Item alias:\ls -Force -ErrorAction SilentlyContinue } | |
| if (Get-Alias grep -ErrorAction SilentlyContinue) { Remove-Item alias:\grep -Force -ErrorAction SilentlyContinue } | |
| # Drop-in modern CLI tools if they are available on the machine | |
| if (Get-Command bat -ErrorAction SilentlyContinue) { Set-Alias cat bat } | |
| if (Get-Command eza -ErrorAction SilentlyContinue) { Set-Alias ls eza } | |
| if (Get-Command fd -ErrorAction SilentlyContinue) { Set-Alias find fd } | |
| # Prioritize ripgrep -> system native grep -> custom object function match | |
| if (Get-Command rg -ErrorAction SilentlyContinue) { | |
| Set-Alias grep rg | |
| } elseif (Get-Command grep -ErrorAction SilentlyContinue) { | |
| # Let system-installed native GNU grep.exe execute directly | |
| } else { | |
| function grep ($pattern, $path="*") { Select-String -Pattern $pattern -Path $path } | |
| } | |
| # ============================================================================== | |
| # 4. Custom Functions & Shortcuts | |
| # ============================================================================== | |
| # Fixed Hist Function (Safe from quote parsing errors) | |
| function hist { | |
| param([string]$Find) | |
| $HistoryPath = (Get-PSReadlineOption).HistorySavePath | |
| if (-not $Find) { | |
| Get-Content $HistoryPath | Get-Unique | more | |
| return | |
| } | |
| Write-Host "Searching history for: $Find" -ForegroundColor Cyan | |
| Get-Content $HistoryPath | Where-Object { $_ -like "*$Find*" } | Get-Unique | more | |
| } | |
| # Git Shortcuts | |
| function gs { git status @args } | |
| function ga { git add @args } | |
| function gc { git commit @args } | |
| function gcm { git commit -m $args[0] } | |
| function gp { git push @args } | |
| # ============================================================================== | |
| # 5. External Tooling Integrations (Chocolatey, etc.) | |
| # ============================================================================== | |
| $ChocolateyProfile = "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" | |
| if (Test-Path($ChocolateyProfile)) { | |
| Import-Module "$ChocolateyProfile" | |
| } |
