I'm working on a Windows program that adds values to the PATH environment variable. The PATH variable is semi-colon (;
) delimited, and any valid folder paths can be included. However, ;
is valid in folder paths, so paths containing semicolons must be surrounded in double quotes when being added to path:
C:\Program Files (x86);"C:\;My Folder";C:\Program Files
To avoid this issue with semicolons, my program wraps all paths in double quotes. However, someone recently informed me that Powershell does not work with quoted values in the PATH variable, as it treats the quotes as ordinary characters:
I looked at how PowerShell handles the Path variable (based on this code: PowerShell/PowerShell@16176ef/src/System.Management.Automation/engine/CommandDiscovery.cs#L1187-L1240). From what I can tell, PowerShell doesn't remove double quotes - it just splits paths wherever it sees a semicolon.
So, the PATH value C:\Program Files (x86);"C:\;My Folder";C:\Program Files
is wrongly interpreted like this (if I correctly understand what's going on):
C:\Program Files (x86)
"C:\
My Folder"
C:\Program Files
Here's a video demonstrating the behavior (this only occurs with Powershell, not with cmd): https://www.mediafire.com/file/aih2ky9fz07x5w0/powershellpathwithsemicolonsbug.mp4/file
What is the best way for my program to deal with this? And, more pressingly, why does Powershell not work with PATH values that have quotes (even though CMD does)?
Editing the path value to include semicolons using the manual way - Edit the system environment variables -> Path -> Edit
- causes Windows to automatically wrap the required paths in quotes, breaking Powershell. So this isn't an issue with my program or situation - it's an inherent problem with how Powershell processes the PATH.
Is this behavior intentional in Powershell? And is there a workaround, or will I have to compromise by either A. Wrapping paths that contain semicolons in quotes and allowing this buggy behavior to occur or B. not allowing semicolons in PATH?
iRon's helpful answer shows you how you can parse the $env:PATH
value manually in a manner that respects double-quoted entries.
As for your questions:
Is this behavior intentional in PowerShell?
I don't think so, and it is arguably a bug, as discussed in GitHub issue #24002; quoting from this comment (of mine):
Indeed, PowerShell doesn't support
"..."
-enclosed entries in$env:PATH
in direct invocation (see below), whereascmd.exe
does.
Note that POSIX-compatible shells such asbash
, like PowerShell, also do not support"..."
-enclosed entries.Arguably, PowerShell and POSIX-compatible shells either should support
"..."
-enclosed entries or support a way to escape the entry-separator character (which is;
on Windows, and:
on Unix-like platforms), but they do not.By contrast, when you use
Start-Process
, as well as the[System.Diagnostics.Process]
API withUseShellExecute = $true
, which is equivalent,"..."
-enclosed entries ARE recognized.This may come down to differences between two WinAPI functions:
CreateProcess()
as used in direct invocation and withUseShellExecute = $false
(the default) in .NET (Core) vs.ShellExecuteEx()
as used byStart-Process
by default and withUseShellExecute = $true
.Given that direct invocation in
cmd.exe
(as opposed to use of the internalstart
command) presumably also usesCreateProcess()
, there may be custom logic incmd.exe
for interpreting%PATH%
.
Is there a workaround?
Assuming that creation of short (8.3) file names is turned on (it is by default), you can take advantage of the fact that a file name containing ;
always has a short name generated for it, even if it otherwise has 8 or fewer chars. in the base name and an extension with 3 or fewer chars and that the resulting short name is guaranteed not to contain ;
or spaces; e.g.:
# -> e.g., 'C:\M_YFOL~1'
$shortFolderPath =
(New-Object -ComObject Scripting.FileSystemObject).GetFolder("C:\;My Folder").ShortPath
In other words:
Replace those $env:PATH
entries that require "..."
enclosure for disambiguation due to embedded ;
with their short (8.3) versions...
... and remove the double quotes around them.
Note that the specific 8.3 names can vary depending on the presence of other folders with similar long names, so they should be determined on each machine.