2

How do I change the default Powershell CLI behavior to ignore file extensions for filetypes that live on my $Env:Path environment variable?

If I just type python, it works correctly and drops me into the Python interpreter as I expect since the extension is part of the environment variable.

The problem is that when I type pyth and tab-complete in PowerShell, it always appends the .exe. I just want it to finish the first part of the command without the extension.

Is this possible? Maybe a script?

  • 2
    If you don’t like file extensions, don’t use Windows. cmd also completes including file extensions. That’s just how things work. – Daniel B Dec 30 '16 at 19:56
  • That's not exactly the answer I was looking for... Is there maybe a way to script the behavior with a Powershell profile or something else? – jmreicha Dec 30 '16 at 20:07
  • Highly unlikely. However, you can take a look at what PSReadLine does and maybe derive a solution. It involves (.NET) programming, of course. – Daniel B Dec 30 '16 at 20:08
  • Interesting. I'll take a look, thanks. – jmreicha Dec 30 '16 at 20:23
  • 3
    What is the problem when you see the complete file name? – guntbert Dec 30 '16 at 20:29
5

You can override the standard tab completion function with your own. In the newest version of PowerShell, that function is TabExpansion2. This modification of it appears to do what you want:

Function TabExpansion2 {
    [CmdletBinding(DefaultParameterSetName = 'ScriptInputSet')]
    Param(
        [Parameter(ParameterSetName = 'ScriptInputSet', Mandatory = $true, Position = 0)]
        [string] $inputScript,

        [Parameter(ParameterSetName = 'ScriptInputSet', Mandatory = $true, Position = 1)]
        [int] $cursorColumn,

        [Parameter(ParameterSetName = 'AstInputSet', Mandatory = $true, Position = 0)]
        [System.Management.Automation.Language.Ast] $ast,

        [Parameter(ParameterSetName = 'AstInputSet', Mandatory = $true, Position = 1)]
        [System.Management.Automation.Language.Token[]] $tokens,

        [Parameter(ParameterSetName = 'AstInputSet', Mandatory = $true, Position = 2)]
        [System.Management.Automation.Language.IScriptPosition] $positionOfCursor,

        [Parameter(ParameterSetName = 'ScriptInputSet', Position = 2)]
        [Parameter(ParameterSetName = 'AstInputSet', Position = 3)]
        [Hashtable] $options = $null
    )

    End
    {
        $source = $null
        if ($psCmdlet.ParameterSetName -eq 'ScriptInputSet')
        {
            $source = [System.Management.Automation.CommandCompletion]::CompleteInput(
                <#inputScript#>  $inputScript,
                <#cursorColumn#> $cursorColumn,
                <#options#>      $options)
        }
        else
        {
            $source = [System.Management.Automation.CommandCompletion]::CompleteInput(
                <#ast#>              $ast,
                <#tokens#>           $tokens,
                <#positionOfCursor#> $positionOfCursor,
                <#options#>          $options)
        }
        $field = [System.Management.Automation.CompletionResult].GetField('completionText', 'Instance, NonPublic')
        $source.CompletionMatches | % {
            If ($_.ResultType -eq 'Command' -and [io.file]::Exists($_.ToolTip)) {
                $field.SetValue($_, [io.path]::GetFileNameWithoutExtension($_.CompletionText))
            }
        }
        Return $source
    }    
}

I added the lines after the one that starts with $field; it goes through the default tab completion options and lops the extension off ones that appear to be from your PATH. I got the original source with this command:

(Get-Command 'TabExpansion2').ScriptBlock

If you put the new function in a .ps1 file and dot-execute that script (e.g. . .\tabnoext.ps1), it will become the tab completion handler for the current session. To load it every time you open a PowerShell window, just paste all that code into a PowerShell profile script.

If you're on an old version of PowerShell, you need to override the TabExpansion function, which just returns an array of strings.

  • This works great, thank you. Is there a way to source files in the powershell profile so that I can separate the function into its own file? – jmreicha Dec 30 '16 at 22:22
  • I just tried adding the dot source to the override script in my profile and it worked, so it looks like the answer is yes. Thanks again. – jmreicha Dec 30 '16 at 22:26
  • Worth noting, I've not been able to get this to work without defining the function as global i.e Function global:TabExpansion2 { – webnoob Nov 16 '18 at 13:06

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.