2

In Windows 10, Shift-right-clicking on a folder or in the background in File Explorer adds an "OpenPowerShell window here" command to the context menu.

However, the command used to open the PowerShell window is ill-defined (as of at least W10 release ID 1709) in that it incorrectly assumes that folder names never contain embedded ' characters:

# !! Breaks with folder names such as "a'b"
powershell.exe -noexit -command Set-Location -literalPath '%V' 

See below for a fix, but note that it requires administrative privileges.

2 Answers 2

1

Update: A standalone command --- no script file dependency.

Copy, paste, and execute this code in a PowerShell console for an instant "proof-of-concept" demo:

$msg = @'
$Args[0] : {0}
$Args[1] : {1}
'@
&{echo ($msg -f $Args[0], $Args[1])} --% I'm an unquoted string with an apostrophe and spaces.

Output:

PS C:\> $msg = @'
>> $Args[0] : {0}
>> $Args[1] : {1}
>> '@
>> &{echo ($msg -f $Args[0], $Args[1])} --% I'm an unqoted string with an apostroohe and spaces.
$Args[0] : --%
$Args[1] : I'm an unqoted string with an apostroohe and spaces.
PS C:\>
  • (interesting that --% is both functional and captured as an argument)

The "magic bullet" is the Stop parsing token: --%. Per the documentation:

The stop-parsing symbol (--%), introduced in PowerShell 3.0, directs PowerShell to refrain from interpreting input as PowerShell commands or expressions.
...
When it encounters a stop-parsing symbol, PowerShell treats the remaining characters in the line as a literal.

Though intended for use with arguments to executables, it also works with arguments to script blocks as the above code demonstrates.

So to execute Set-Location with an unquoted path, the syntax is:

    &{Set-Location -LiteralPath $Args[1]} --% <unquoted path>  

And thus our registry command becomes:

    powershell.exe -NoExit -Command &{Set-Location -LiteralPath $Args[1]} --%% %V  
  • Note the doubled percent sign (%%) is needed to produce the literal % symbol in the resulting command.

To modify the command machine-wide, edit the registry directly. The relevant keys are:

  • HKLM\SOFTWARE\Classes\Directory\Background\Shell\PowerShell\Command
  • HKLM\SOFTWARE\Classes\Directory\Shell\PowerShell\Command
  1. Take ownership of the key and assign yourself full control.

  2. Edit the (Default) value, changing it to:

     powershell.exe -NoExit -Command &{Set-Location -LiteralPath $Args[1]} --%% %V  
    
  3. Remove the Full Control permission you added for your user.

  4. Change owner back to TrustedInstaller by speecifying NT Service\TrustedInstaller as the username in the steps you followed to take ownership.

To modify the command on a per-user basis, simply create and edit the registry keys:

  • HKCU\Softwar\Classes\Directory\Background\Shell\PowerShell\Command

  • HKCU\Software\Classes\Directory\Shell\PowerShell\Command
    Changing the value of (Default) to:

     powershell.exe -NoExit -Command &{Set-Location -LiteralPath $Args[1]} --%% %V  
    

or just copy, pasee, and execute the follwoing:

'Background\','' | ForEach{
    $splat = @{
        'Path'    = ('HKCU:\Software\Classes\Directory\{0}Shell\PowerShell\Command' -f $_)
        'Value'   = 'powershell.exe -NoExit -Command &{Set-Location -LiteralPath $Args[1]} --%% %V'
    }
    New-Item @splat -Force
}


Original Reply

For a 5.1 workaround, I can't think of a way with just a PowerShell command line, but calling a one-line script seems to do the trick:

(Code edit/improvement per @mklement0's comment)

### OpenHere.ps1
Set-Location -LiteralPath $Args[0]

### (HKCU|HKLM)\Software\Classes\Direcory[\Background]\Shell\PowerShell\Command
###
###powershell.exe -noexit -File "C:\Path\to\OpenHere.ps1" "%V"
###
  • Save as "OpenHere.ps1" to an appropriate location
  • You can then modify either:
    • HKLM\SOFTWARE\Classes\Directory\Shell\PowerShell\Command
      if you have Admin access and are comfortable dealing with ownership and permissions.
      Otherwise, you can create per-user entries under:
    • HKCU\Software\Classes\Directory\Shell\PowerShell\Command

The syntax for the registry command line is:

  • powershell.exe -noexit -File "C:\Path\to\OpenHere.ps1" "%V"

Here's a "self-installing" version of the above code that will create the context menu entries under HKCU(per-user mod).

  • Save the following as a .ps1 file in the directory where it will reside.
  • Run the script from a PowerShell console with no arguments. The code uses the current Path\FileName of the .ps1 file in the command line it creates.
### OpenHere.ps1
If ($Args) {   ### Launched from context menu
    Set-Location -LiteralPath $Args[0]
} Else     {   ### Create HKCU registry entries
    'Background\','' | ForEach {
        $splat = @{
            'Path'   = ('HKCU:\Software\Classes\Directory\{0}Shell\PowerShell\Command' -f $_)
            'Value'  = ('powershell.exe -NoExit -File "{0}" "%V"' -f $PSCommandPath)
            'Type'   = 'ExpandString'
        }
        New-Item @splat -Force
    }
}

###   The "(Default)" value is created as a REG_EXPAND_SZ to allow for subsequent
###   editing that can include environmental variables

5
  • 1
    @mklement0: Thanks. Edited with your suggested improvement. You made me Goggle " whitespace normalization" !!! :D Dec 21, 2023 at 4:48
  • 1
    :) Nice touch to make the script self-installing.
    – mklement0
    Dec 21, 2023 at 6:52
  • 1
    On further reflection, I think your answer deserves to be the accepted one. Nice job.
    – mklement0
    Dec 21, 2023 at 18:19
  • 1
    @mklement0: Thanks! I couldn't let it go and I figured out a standalone command that doesn't depend on a script file! Will update answer tomorrow (posting this from phone). Dec 22, 2023 at 7:08
  • Nicely done; didn't know that --% also worked with script blocks (albeit quirkily).
    – mklement0
    Jan 23 at 18:21
2

Preface

  • Keith Miller's helpful answer is a pragmatic, automated solution - the only thing to note is that it only works at the user level.

  • The answer below may be of interest for an all-users solution and for technical background; the OpenHere.ps1 from Keith's answer can equally be used in an all-users solution (except for the self-installing part).


Tip of the hat to LesFerch for all his input.

Note:

  • This fix requires administrative privileges (running with elevation).

Open regedit.exe and apply the following steps to both of the following registry keys: HKEY_CLASSES_ROOT\Directory\shell\Powershell\command and
HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell\command:

  • Preparation: modify the permissions so that modification of the value (the PowerShell command) becomes possible:

    • Alternatives to manual permission modification:

      • @LesFerch mentions the following third-party utilities as alternatives to manual permission modification, by allowing you to run regedit.exe directly as the NT SERVICE\TrustedInstaller user:

        PowerRun (my preferred), AdvancedRun. This is a much safer option than messing with permissions (which people often get wrong and break things).

      • Keith Miller's helpful answer mentions defining user-level keys as an alternative - which doesn't require elevation, but limits the solution to the current user.

    • Right-click on the command subkey and select Permissions...

    • Click on Advanced and:

      • make the Administrators group the owner of the key
      • give the Administrators group full control of the key
    • Note: I am not aware of any adverse effects of these modifications, but do tell us if you know of any.
      However, to be safe, you may revert these modifications after modifying the command as described below, which entails restoring the TrustedInstaller security principal as the owner of the command key; note that you must specify it as
      NT SERVICE\TrustedInstaller.

  • Now replace the command key's (Default) value with the following (see the note re console settings / colors below):

    cmd /c set "_dir=%V%" & powershell.exe -NoExit -Command Set-Location -LiteralPath $env:_dir
    
    • Note:

      • By virtue of calling via cmd, you'll get the latter's console settings instead of Windows PowerShell's, which notably include a blue background.

        • You can avoid this by placing start before powershell.exe, which opens a new window with the usual settings and colors, but the downside is that the original, transient window that gets invariably created for cmd.exe briefly flashes on the screen.

        • Keith Miller's answer offers an alternative via a helper .ps1 script, which allows direct invocation via powershell.exe -File and therefore avoids the flashing problem; the -File CLI parameter treats its arguments literally, so the problem described below when using -Command doesn't apply.

          powershell.exe -NoExit -File "C:\Path\to\OpenHere.ps1" "%V"
          
      • Calling via cmd /c is the most robust and safe option:

        • The value of %V could only syntactically break the set command if it contained " characters, which is by definition impossible (file and folder names cannot contain ").

        • However, if a folder name contains something that looks like a cmd.exe environment-variable reference verbatim (e.g. %OS%) and the referenced variable exists, the reference is expanded, causing changing to that folder to either fail or - hypothetically - a different folder to be targeted.

        • The reason that the command cannot just use cd as part of the cmd.exe call is that powershell.exe malfunctions when called from a folder whose name happens to contain [ or ] (e.g. foo[0]). This bug has been fixed in the PowerShell (Core) 7+ CLI, pwsh.exe, so there you could simply use:

          # PowerShell 7+
          cmd /c cd /d "%V" & pwsh.exe
          
          • In fact, pwsh.exe's new -WorkingDirectory parameter enables direct invocation (this is not only more efficient, but also avoid the console-settings problem):[1]

            pwsh.exe -WorkingDirectory "%V\."
            
      • While calling powershell.exe directly is an option, it invariably involves tradeoffs:

        • If your folder names may contain literal ' characters, but NEVER contain literal ` or $ characters, you can use "..." (an expandable (interpolating) string) instead, but this comes with the following CAVEAT:

          • While the command would simply malfunction with verbatim folder names such as foo`bar or $foo (or - hypothetically - target a different directory), it could result in unwanted execution of commands, via carefully - maliciously - crafted folder names that contain $(...) subexpressions.

            powershell.exe -NoExit -Command "Set-Location -LiteralPath \"%V\""
            

It should be possible to script the above steps.


[1] \. is appended is so to ensure that root paths such as C:\ are properly handled too; otherwise, the PowerShell CLI would (justifiably) interpret "C:\", for instance, as C: followed by an escaped " character, i.e., in effect as verbatim C:", which breaks.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .