cd /news/developer-tools/make-windows-feel-more-like-zsh · home topics developer-tools article
[ARTICLE · art-32707] src=gist.github.com ↗ pub= topic=developer-tools verified=true sentiment=· neutral

make windows feel more like zsh

A developer published a PowerShell configuration script that transforms Windows PowerShell into a Unix-like shell environment. The script replaces the default PSReadLine module, integrates Starship prompt and Zoxide directory jumper, and implements Unix-style commands such as 'la', 'll', 'which', 'touch', 'ln', 'alias', 'export', and 'ps'.

read6 min views1 publishedJun 8, 2026

| # ============================================================================== | | | # 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" | | | } |

── more in #developer-tools 4 stories · sorted by recency
── more on @powershell 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/make-windows-feel-mo…] indexed:0 read:6min 2026-06-08 ·