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'. | ============================================================================== | | | 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" | | | } |