(Which)y Business
Inspired by the which
command, I sought out to solve a few problems:
- Determine the source of a command
> which git C:\Program Files\Git\cmd\git.exe
- Resolve the source of aliases (including nested aliases)
> Set-Alias a b > Set-Alias b c > Set-Alias c d > Set-Alias d cd > which a Alias: b Alias: c Alias: d Alias: cd Alias: Set-Location Set-Location [[-Path] <string>] [-PassThru] [-UseTransaction] [<CommonParameters>] Set-Location -LiteralPath <string> [-PassThru] [-UseTransaction] [<CommonParameters>] Set-Location [-PassThru] [-StackName <string>] [-UseTransaction] [<CommonParameters>]
- Provide detailed information for commands
> which Set-Location -v VERBOSE: Set-Location [[-Path] <string>] [-PassThru] [-UseTransaction] [<CommonParameters>] Set-Location -LiteralPath <string> [-PassThru] [-UseTransaction] [<CommonParameters>] Set-Location [-PassThru] [-StackName <string>] [-UseTransaction] [<CommonParameters>] Implemented by Microsoft.PowerShell.Commands.SetLocationCommand Defined in C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.Management\ v4.0_3.0.0.0__31bf3856ad364e35\Microsoft.PowerShell.Commands.Management.dll
- Supply detailed information about the implementation of commands which I can edit.
> which bundler C:\Ruby24-x64\bin\bundler.bat > which bundler -v C:\Ruby24-x64\bin\bundler.bat VERBOSE: @ECHO OFF IF NOT "%~f0" == "~f0" GOTO :WinNT @"C:\Ruby24-x64\bin\ruby.exe" "C:/Ruby24-x64/bin/bundler" %1 %2 %3 %4 %5 %6 %7 %8 %9 GOTO :EOF :WinNT @"C:\Ruby24-x64\bin\ruby.exe" "%~dpn0" %*
- For powershell functions, tell me exactly where they are implemented (file, lines, modules).
>which which -v VERBOSE: In C:\Users\<user>\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 line(s) 29-102: function which { ... }
- For editable scripts, allow them to be openend in a text editor
> which which -editor vim # opens my profile in Vim to line 29
The implementation is recursive and relatively simple. Current version is on gist.github.com/idavis. This version supports Vim and VS Code.
function which {
[CmdletBinding()]
param([string]$name, [Parameter(Mandatory=$false)][ValidateSet('code','vim')][string]$editor = "")
$commands = @(get-command $name -ErrorAction SilentlyContinue)
if($commands.Length -eq 0) {
Write-Error "Unknown command or alias $name."
}
if($commands.Length -gt 1) {
Write-Error "Ambiguous command or alias $name. Found:"
$commands | % { Write-Error "`t$($_)" }
}
function Write-NonVerbose {
if(!$verbose) {
Write-Host $cmd.Definition
}
}
$cmd = $commands[0]
$verbose = $PSBoundParameters['Verbose'] -eq $true
switch($cmd.CommandType) {
"Alias" {
Write-Host "$($cmd.CommandType):`t$($cmd.Definition)`n"
which "$($cmd.Definition)" -Verbose:$verbose
}
"Cmdlet" {
Write-NonVerbose
Write-Verbose "`n$($cmd.Definition)`nImplemented by $($cmd.ImplementingType)`nDefined in $($cmd.DLL)"
}
"Function" {
Write-NonVerbose
$block = $cmd.ScriptBlock
$content = $block.StartPosition.Content
$start = $block.StartPosition.StartLine
$end = $block.StartPosition.EndLine
if($block.Module) {
Write-Verbose "`nIn $($block.Module.Path)`nIn $($block.File) line(s) $($start)-$($end):`n`n$($content)"
} else {
Write-Verbose "`nIn $($block.File) line(s) $($start)-$($end):`n`n$($content)"
}
switch ($editor) {
"code" { & code --goto "`"$($block.File)`":$($start):$($block.StartPosition.StartColumn)" }
"vim" { & vim "+$($block.StartPosition.StartLine)" "$($block.File)" }
Default { }
}
}
"Application" {
$extension = [System.IO.Path]::GetExtension($cmd.Definition)
Write-Host $cmd.Definition
function Open([string]$fileName) {
switch ($editor) {
"code" { & code --goto "$fileName" }
"vim" { & vim "$fileName" }
Default { }
}
}
switch($extension) {
".cmd" {
Write-Verbose ("`n" + (Get-Content $cmd.Definition -Raw))
Open $cmd.Definition
}
".bat" {
Write-Verbose ("`n" + (Get-Content $cmd.Definition -Raw))
Open $cmd.Definition
}
default {
# Don't know what to do for other extensions.
}
}
}
default {
Write-Host $cmd.Definition
}
}
}