cdpath.psm1
using namespace System.Management.Automation using namespace System.Management.Automation.Language Set-StrictMode -Off [string] $script:PathFile = '~/.cdpath' [string[]]$script:cdpath = $null $script:Options = [pscustomobject] @{ SetWindowTitle = $true } $script:PreviousWorkingDir = '~' if(-not (Test-Path $script:PathFile)) { Set-Content -Path $script:PathFile -Value '' } class CDPathCompleter : System.Management.Automation.IArgumentCompleter { [System.Collections.Generic.IEnumerable[System.Management.Automation.CompletionResult]] CompleteRemaining( [string] $wordToComplete, [System.Management.Automation.Language.CommandAst] $commandAst, [System.Collections.IDictionary] $fakeBoundParameters) { $cdpath = ,'.' + $script:cdpath $ce = $commandAst.CommandElements[1] if ($ce -isnot [StringConstantExpressionAst]) { return $null } $el = $commandAst.CommandElements $count = ($el.Count - 1) if ($wordToComplete){ $count-- } if($count -eq 1){ $v = $el[1].Value if($v.EndsWith('\')) { $pattern = "$v\$wordtoComplete*" } else { $pattern = $v + "*\$wordtoComplete*" } } else{ $v = $el[1..$count].Value $pattern = $v -join '*\' $pattern += "*\$wordtoComplete*" } $res = [System.Collections.Generic.List[CompletionResult]]::new(10) if([io.path]::IsPathRooted($pattern)) { foreach($r in Resolve-Path $pattern) { $name = $r.Substring(0,$v.length) $res.Add([CompletionResult]::new($name,$name, [CompletionResultType]::ProviderContainer, $name)) } } else{ Join-Path -Path $cdpath -ChildPath $pattern -Resolve -ea:SilentlyContinue | foreach { if ([io.Directory]::Exists($_)) { $name = [io.path]::GetFileName($_) if ($name.Contains(' ')){ $name = "'$name'" } $res.Add([CompletionResult]::new($name,$name, [CompletionResultType]::ProviderContainer, $name)) } } } return $res } [System.Collections.Generic.IEnumerable[CompletionResult]] CompletePath([string] $wordToComplete, [CommandAst] $commandAst, [System.Collections.IDictionary] $fakeBoundParameters) { if ($commandAst.CommandElements.Count -ne 2) { return $null } $cdpath = ,'.' + $script:cdpath $ce = $commandAst.CommandElements[1] if ($ce -isnot [StringConstantExpressionAst]) { return $null } $pattern = $commandAst.CommandElements[1].Value $res = [System.Collections.Generic.List[CompletionResult]]::new(10) foreach($r in [CompletionCompleters]::CompleteFilename("$wordToComplete*")) { if($_.ResultType -eq [CompletionResultType]::ProviderContainer) { $res.Add($r) } } foreach($r in Join-Path -Path $cdpath -ChildPath "$pattern*" -Resolve -ea:SilentlyContinue) { if ([io.Directory]::Exists($r)) { if ($r.Contains(' ')){ $r = "'$r'" } $res.Add([CompletionResult]::new($r,$r, [CompletionResultType]::ProviderContainer, $r)) } } return $res } [System.Collections.Generic.IEnumerable[System.Management.Automation.CompletionResult]] CompleteArgument([string] $commandName, [string] $parameterName, [string] $wordToComplete, [System.Management.Automation.Language.CommandAst] $commandAst, [System.Collections.IDictionary] $fakeBoundParameters) { switch($parameterName) { 'Path' {return $this.CompletePath($wordToComplete, $commandAst, $fakeBoundParameters);break} 'Remaining' {return $this.CompleteRemaining($wordToComplete, $commandAst, $fakeBoundParameters)} } return $null } } <# .SYNOPSIS Changing location using by resolving a pattern agains a set of paths .DESCRIPTION CDPath replaces the 'cd' alias with Set-CDPathLocation Parts of paths can be specified with a space. PS> cd win mo will change directory to ~/documents/windowspowershell/modules .EXAMPLE Set-CDPathLocation .... The example changes the current location to the parent three levels up. .. (first parent) ... (second parent) .... (third parent) .EXAMPLE Set-CDLocation 'C:\Program Files (X86)\Microsoft Visual Studio 12' Set-CDLocation ~ # Go back to 'C:\Program Files (X86)\Microsoft Visual Studio 12' Set-CDPathLocation - .EXAMPLE Get-CDPath ~/Documents/GitHub ~/Documents ~/corpsrc # go to ~/documents/WindowsPowerShell/Modules/CDPath Set-CDLocation win mod cdp #> function Set-CDPathLocation { param( [Parameter(Position=0)] [ArgumentCompleter([CDPathCompleter])] [string] $Path, [Parameter(ValueFromRemainingArguments)] [ArgumentCompleter([CDPathCompleter])] [string[]] $Remaining=$null, [switch] $Exact ) $CurrentWorkingDir = $Pwd.ProviderPath $NewPath = $null # If there are extra arguments, create a globbing expression # so that cd a b c => cd a*\b*\c* if ($Remaining) { $ofs='*\' $path= "$path*\$Remaining*" } # Resolve Path if (!$Path) { $Path = '~' } # .'ing shortcuts for ... and .... if ($Path -match "^\.{3,}$") { $Path = $path.Substring(1) -replace '\.','..\' } if ($Path -eq '-') { # pop back to your old Path Set-Location $script:PreviousWorkingDir $script:PreviousWorkingDir = $CurrentWorkingDir return } # See if the location exists. If it does not, use $CDPath to # resolve to a location that exists if (Test-Path $Path -ea SilentlyContinue) { $NewPath = $Path } else { if ($Exact) { $paths = Join-Path $cdpath "$path" } else { $paths = Join-Path $cdpath "$path*" -Resolve -ea SilentlyContinue } if ($paths) { if ($paths -is [string]) { $NewPath=$paths } else { # find the first match that is a container $res=Test-Path -PathType:Container $paths for($i=0; $i -lt $res.Length;++$i) { if ($res[$i]) { $NewPath=$paths[$i] break } #if } #for } } #if $paths # clear $newpath if it is not a directory if($NewPath -and !(Test-Path $NewPath -PathType:Container)) { $NewPath=$null } }#else if ($NewPath ) { $script:PreviousWorkingDir = $Pwd.ProviderPath $newPath = (Resolve-Path -path $NewPath).ProviderPath Set-Location -Path $NewPath if($script:Options.SetWindowTitle) { $Host.UI.RawUI.WindowTitle=$NewPath } } else { if (Test-Path $path -ea SilentlyContinue) { Set-Location $path } else { Write-Error "Directory not found: $path" } } } <# .SYNOPSIS Read the cdpath from ~/.cdpath .DESCRIPTION This is useful if you have modified your path file with an external editor #> function Update-Cdpath { $script:cdpath=Get-Content -Path $script:PathFile | Where-Object {$_.Trim()} | ForEach-Object { $ExecutionContext.InvokeCommand.ExpandString($_) } } <# .SYNOPSIS Adds one or more paths to your cdpath .DESCRIPTION This is useful if you have modified your path file with an external editor #> function Add-CDPath { param( [string[]] $Path , # Specify Prepend to add the paths before the existing paths [Switch] $Prepend ) if ($Prepend) { Set-CDPath @($Path + $script:cdpath) } else { Set-CDPath @($script:cdpath + $Path) } } function Get-CDPath { $script:cdpath } function Get-CDPathOption { $script:Options } function Set-CDPath { param( [Parameter(Mandatory)] [string[]] $Path ) $script:CDPath = $Path $ofs = [Environment]::NewLine Set-Content -Path $script:PathFile -value $Path } <# .SYNOPSIS Customizes the behavior of Set-CDPathLocation in CDPath #> function Set-CDPathOption { param( # Indicates if the the window title should be changed when changing location [Switch] $SetWindowTitle ) if($PSBoundParameters.ContainsKey('SetWindowTitle')) { $script:Options.SetWindowTitle = [bool] $SetWindowTitle } } # read cdpath from file Update-Cdpath if ($script:cdpath -EQ $null) { Set-CDPath ~,~/documents,$env:programfiles } Set-Alias -Name cd -Value Set-CDPathLocation -Description 'Use fast navigation between the path defined by cdpath.txt' # SIG # Begin signature block # MIINLAYJKoZIhvcNAQcCoIINHTCCDRkCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUBIkLF5UzCvjn6OuBJu2+AHO5 # 6q6gggpuMIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0B # AQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk # IElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQg # Q29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA # +NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ # 1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0 # sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6s # cKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4Tz # rGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg # 0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIB # ADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUH # AQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYI # KwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz # c3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0 # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaG # NGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD # QS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0 # dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYE # FFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6en # IZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06 # GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5j # DhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgC # PC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIy # sjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4Gb # T8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHvMIIFNjCC # BB6gAwIBAgIQC00BIFzepyXTcMbB+AfaoDANBgkqhkiG9w0BAQsFADByMQswCQYD # VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGln # aWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29k # ZSBTaWduaW5nIENBMB4XDTE1MDgxMTAwMDAwMFoXDTE4MDgxNTEyMDAwMFowfTEL # MAkGA1UEBhMCU0UxFzAVBgNVBAgTDlN0b2NraG9sbXMgTGFuMREwDwYDVQQHDAhT # a8O2bmRhbDEgMB4GA1UEChMXUG93ZXJDb2RlIENvbnN1bHRpbmcgQUIxIDAeBgNV # BAMTF1Bvd2VyQ29kZSBDb25zdWx0aW5nIEFCMIIBIjANBgkqhkiG9w0BAQEFAAOC # AQ8AMIIBCgKCAQEA0Yu390VGptjUZIdLxIYQCSG1atPh/tN6qPf/eovS3ohJy+td # XluaPkUXuE3fWeMp+p3Jsj3c/LdsA1iYiQQQnJ/9afqiW2hnmSkNZfgcLQ9mceXl # mmd2otcwfkVhfA6ZuFnpceFgKciLLld7AY1sMiSyc1L5RvEsOR/1S6uBg0AWoSSv # l44vF8EgeArhPCx8GNbUmYfEpeqs1f5LOLlQBwImGCsjv1rmbuPwt0E229XVLerU # auNXJjb+jtrGBTzD384QiLMGtNHWnE4yBStjrLTHHNz4mE0g6jUIYJbFvGLD04Cl # 7WovOkODL+9nmsyreUg2UVBKrO98GK/G8XhG8wIDAQABo4IBuzCCAbcwHwYDVR0j # BBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0OBBYEFAGtca5rM/ejkFdY # 3OVLhIxGkpIBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzB3 # BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1h # c3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2NybDQuZGlnaWNlcnQuY29t # L3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwQgYDVR0gBDswOTA3BglghkgBhv1sAwEw # KjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCBhAYI # KwYBBQUHAQEEeDB2MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j # b20wTgYIKwYBBQUHMAKGQmh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp # Q2VydFNIQTJBc3N1cmVkSURDb2RlU2lnbmluZ0NBLmNydDAMBgNVHRMBAf8EAjAA # MA0GCSqGSIb3DQEBCwUAA4IBAQC+nkBDDZo+AvHQdEm2IA9ygsJIQccvEE/ijFoq # RHqje75atqGhfzcVyqjyRdhj7FJ2WGvcWsHahViDq9ZR5W42S6D9OG0c+Wmo6buc # jn1Y8lfaWIngCloNZ0BF7rBcVmR8JLkfKjVZSsHdbBKVxj3x7NYw9cr7nx/UQjXX # jghqtWMetHOlw73BKpDEJGs7LFtb8TZcNLVyWU4GuW1HWhncf5lq2b3WUP82xKIa # Pf7w50P33/xxo+N12nNJJQ82aIhUVhVXcZ+TWlfO2n0yZT7dsa1FFwRf/dGPIpZm # Kn8tanILjQg5K4NWlli2iLonrmRmqXAlXe8NSPTXWPvaNskAMYICKDCCAiQCAQEw # gYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE # CxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1 # cmVkIElEIENvZGUgU2lnbmluZyBDQQIQC00BIFzepyXTcMbB+AfaoDAJBgUrDgMC # GgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYK # KwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG # 9w0BCQQxFgQU+zMYhbX9RtZqvVbTZrar73M3AcQwDQYJKoZIhvcNAQEBBQAEggEA # YOfq26CF2zPRTR+VOTcZTRbYI7XMV2J4Ys/FlD2Ehvt1G7WDathpKItoEuI36Zco # ZhZbxC0nUEAjWdXCU2yzweHKP/+EIRq6Iwt8HqupvDCYu3IUltsYoqhMKEVtBaZG # 8cFxZYo0Rxbf2Fzn39CIbozhQxxZ0YOpOOHJxOaWJxYoFe/Tzfaq+MNfwUkzTH5L # mFzh25qMxwyKPm4dLuP5eEYG04AyLTFvLSPms3F/XNCgtsfT85IQsJekGWDMIsQy # ijWfJ7F7qP85c4xaiCisqNtrlsXGK7Tbv++I+20JCn9c5bB50MZeZeDDlc30tnNs # zTqQ4hJWC+nlkgyUzNabSw== # SIG # End signature block |