Home > Software design >  Tab completion problem using start in Powershell
Tab completion problem using start in Powershell

Time:10-05

I want to open [test].txt in powershell, but when I press tab on keyboard, it autocompletes like this:

start '.\`[test`].txt'

and generates this error:

start: Could not run this command due to error: The system cannot find the file specified. At line: 1 character: 1 start '.`[test`].txt' ~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo: InvalidOperation: (:) [Start-Process], InvalidOperationException FullQualifiedErrorId: InvalidOperationException, Microsoft.PowerShell.Commands.StartProcessCommand

I want to remove the backtick characters when I press tab. How can I do this?

CodePudding user response:

start (an alias for Start-Process) is really designed to launch executables or external commands.

For invoking the default shell handler associated with an arbitrary file, use Invoke-Item instead!

In order to avoid PowerShell attempting to interpret [...] as a wildcard pattern, use the -LiteralPath parameter:

Invoke-Item -LiteralPath '.\[test].txt'

All item provider cmdlets (*-Item, *-ChildItem) supports literal path binding, so it's always the best/safer choice when you have an exact path.

CodePudding user response:

To complement Mathias R. Jessen's helpful answer.

What you're seeing is a limitation in PowerShell's tab-completion (up to at least PowerShell Core 7.2.0-preview.9):

  • You're tab-completing a value for the - positionally implied - -FilePath parameter of the Start-Process cmdlet (start is built-in alias for it).

  • Since -FilePath only accepts literal (verbatim) file paths, not also wildcard patterns, tab-completion should result in .\[test].txt, without the `-escaping of wildcard-pattern metacharacters [ and ].

By contrast, Invoke-Item (whose built-in alias is ii) does support wildcard patterns via its -Path parameter - as well as literal (verbatim) paths via its -LiteralPath parameter:

  • Since -Path is in Invoke-Item's default parameter set and is the first positional parameter, the tab-completed value would have worked as-is had you used Invoke-Item rather than Start-Process:

    # OK with tab-completion: positionally binds to -Path
    Invoke-Item '.\`[test`].txt'
    
  • In fact, tab-completion is aware of when the argument at hand will be bound to a -LiteralPath parameter and then does not perform escaping:

    # OK with tab-completion: due to explicitly targeting -LiteralPath,
    # no escaping is performed.
    Invoke-Item -LiteralPath .\[test].txt 
    
  • The above applies to all provider cmdlets, such as Get-ChildItem and Get-Content (roughly speaking, they comprise the cmdlets that have the word Item, Content, or Property in their name).


Potential future enhancement:

  • PowerShell's tab-completion logic is currently hard-wired to:

    • default to escaped completion,
    • except if the target parameter name (if known) happens to be -LiteralPath
  • GitHub issue #14149 proposes:

    • defaulting to literal completion.
    • except if the target parameter (if known) explicitly signals support for wildcard patterns via the SupportsWildcardsAttribute

When to use Start-Process vs. Invoke-Item:

  • Simply put, Start-Process is a superset of Invoke-Item; the former is typically used to launch GUI executables, whereas the latter is typically used to open documents with the application registered to handle them or directories in File Explorer (on Windows).

  • While Start-Process can do everything Invoke-Item can do and more, a crucial difference which amounts to a pitfall is that Start-Process looks for a file or directory identified by name only in the directories listed in $env:PATH first rather than only in the current directory; prefixing the name with .\ (or using a full path) avoids this ambiguity.

# Unambiguously targets .\file.txt, i.e. a file in the *current* dir.
# In other words: The same as:
#   Invoke-Item .\file.txt
Invoke-Item file.txt

# AMBIGUOUS:
# !! *First* looks for a file.txt file in the directories
# !! listed in $env:PATH and opens the first one it finds.
# !! Only if none is found does it look in the current dir.
Start-Process file.txt

# OK - unambiguously looks in the current dir. only, thanks to ".\"
Start-Process .\file.txt

Note:

  • Since Start-Process is typically used to launch executables (GUI executables; except for special situations, it is the wrong tool for launching console applications), looking in $env:PATH (first) makes sense, but that the behavior also extends to documents (non-executables) is unexpected and problematic - see GitHub issue #9469.

On the plus side, Start-Process (unlike Invoke-Item) can open URLs in the default web browser; e.g.:

Start-Process http://example.org

In summary:

  • It is fine to always use Start-Process in lieu of Invoke-Item, as long as you avoid ambiguity by prefixing target items in the current directory with .\ - fortunately, tab-completion for items in the current dir. automatically adds this prefix.
  • Related