tl;dr
REM From cmd.exe (Command Prompt) / a batch file:
powershell.exe -c Start-Process cmd.exe -Verb RunAs ('/k {0} \"a\" \"B C\" \"d\"' -f ((Convert-Path 'TestEnv.cmd') -replace ' ', '^^ '))
If you also want to ensure that the batch file executes in the caller's current directory:
REM From cmd.exe (Command Prompt) / a batch file:
powershell.exe -c Start-Process cmd.exe -Verb RunAs ('/k cd \"{0}\" ^&^& {1} \"a\" \"B C\" \"d\"' -f $PWD.ProviderPath, ((Convert-Path 'TestEnv.cmd') -replace ' ', '^^ '))
Read on for an explanation.
The main problem is:
When you pass a batch-file path directly - as the executable - to Start-Process -Verb RunAs, there seems to be a problem with passing an argument list that contains double quotes (") - I don't know the reason, but the problem occurs in a layer below PowerShell, either in the underlying System.Diagnostics.ProcessStartInfo .NET API or possibly even in the underlying ShellExecuteExe WinAPI function.
(If you either need no arguments or none of them require ", you can pass the batch-file path directly as the executable; by default, because the underlying executable is cmd.exe, which is located in C:\Windows\System32, C:\Windows\System32 becomes the working directory, which you can override with -WorkingDirectory, but note that you must then refer to the batch file with a (possibly relative) path, if it is located elsewhere.)
Workaround:
Instead of using the batch-file path directly as the executable to launch, use cmd.exe as the executable, and pass the batch-file path and all its arguments as arguments to the /c option (or /k, if you want to keep the new window open).
Unfortunately, with -Verb Runas, cmd.exe invariably uses C:\Windows\System32 as the new process' working directory - passing a startup directory with the -WorkingDirectory parameter is then ignored.
- The same applies to
powershell.exe, but, curiously, not to pwsh.exe (PowerShell [Core] v6+) and .NET-based executables, which (a) preserve the caller's working directory by default and (b) do honor a -WorkingDirectory argument.
Therefore, if your TestEnv.cmd batch file is located in the current directory - as opposed to a directory listed in the PATH environment variable ($env:PATH) - you must pass the batch file's full path to cmd.exe, which you can determine with Convert-Path.
Note: If you batch file is in the PATH, so to speak, this step isn't necessary.
Unfortunately, another workaround is then needed, in case the batch file's full path contains spaces: Because "..."-enclosing the batch-file path inexplicably fails, the individual spaces must be ^-escaped instead.
From inside PowerShell this means your command must look like this (note that I'm using /k to keep the new window open, for diagnostic purposes; use /c to auto-close the window when the batch file exits):
# From inside PowerShell:
Start-Process cmd.exe -Verb RunAs (
'/k {0} "a" "B C" "d"' -f ((Convert-Path 'TestEnv.cmd') -replace ' ', '^ ')
)
Caveat: Since cmd.exe is being invoked, C:\Windows\System32 is the working directory.
If you also want to ensure that the batch file executes in the caller's current directory, you must prepend a cd command:
# From inside PowerShell:
Start-Process cmd.exe -Verb RunAs (
'/k cd "{0}" && {1} "a" "B C" "d"' -f $PWD.ProviderPath, ((Convert-Path 'TestEnv.cmd') -replace ' ', '^ ')
)
Calling this from cmd.exe (Command Prompt) / a batch file, via Windows PowerShell's CLI, powershell.exe (note that in PowerShell [Core] v6+ it is now pwsh), adds additional complexity:
The " chars. must be escaped as \" so that PowerShell recognizes as them as part of the command to execute rather than as syntactic quoting around the CLI arguments.
^ must be escaped as ^^ and, in the second command below, & as ^& to prevent cmd.exe from interpreting these characters (up front).
REM From cmd.exe (Command Prompt) / a batch file:
powershell.exe -c Start-Process cmd.exe -Verb RunAs ('/k {0} \"a\" \"B C\" \"d\"' -f ((Convert-Path 'TestEnv.cmd') -replace ' ', '^^ '))
If you also want to ensure that the batch file executes in the caller's current directory:
REM From cmd.exe (Command Prompt) / a batch file:
powershell.exe -c Start-Process cmd.exe -Verb RunAs ('/k cd \"{0}\" ^&^& {1} \"a\" \"B C\" \"d\"' -f $PWD.ProviderPath, ((Convert-Path 'TestEnv.cmd') -replace ' ', '^^ '))