I've got a script that eventually needs to start a batch file. The command I'm running is:
Start-Process "cmd.exe" ".\Batch.bat" -Verb runas
When I run this, the Command Prompt window opens, and it opens to the correct folder, but doesn't actually start the batch file. Once the window opens, I can manually enter:
.\Batch.bat
and that seems to work from there. Also, if I try to bypass CMD entirely and run the batch file in PowerShell, it gives me the first three lines that display when I run it in CMD, but doesn't give me the first prompt I need to enter, so that's why I'm trying to do this in a new CMD window. Any idea what I'm missing?
tl;dr
To run the batch file in an elevated session in a new window that uses the same working directory as the caller and stays open:
Start-Process cmd.exe "/k cd `"$PWD`" & .\Batch.bat" -Verb RunAs
Note the use of /k
, which instructs cmd.exe
to execute the commands that come after it, and then keeps the session open (by contrast, /c
would exit the session after the commands complete, which would automatically close the window - run cmd /?
for details).
If you try to pass a command without preceding it with either /c
or /k
, cmd.exe
effectively ignores the command and enters an interactive session, which is what you saw.
To invoke the batch file in an auto-closing window in the C:\Windows\System32
directory, you can invoke it directly:
Start-Process '.\Batch.bat' -Verb RunAs
Windows treats batch files (*.cmd
, *.bat
) as if they were executables, so you don't necessarily need to invoke them via cmd.exe
explicitly:
Start-Process '.\Batch.bat' -Verb RunAs
The above is the equivalent of cmd.exe /c .\Batch.bat
, which means that the batch file executes and the cmd.exe
session then exits, i.e. the window automatically closes.
If you want to execute the batch file in a cmd.exe
session that stays open, you must use an explicit invocation of cmd.exe
with /k
(run cmd /?
for details):
Start-Process cmd.exe "/k `"$PWD\Batch.bat`"" -Verb RunAs
Note the use of $PWD
to refer to the caller's current directory, which is necessary, because -Verb RunAs
defaults to C:\Windows\System32
as the working directory when -Verb RunAs
is used to invoke unmanaged (non-.NET) applications.
That is, Start-Process
with -Verb RunAs
:
never preserves the caller's working directory and never honors a -WorkingDirectory
argument when invoking unmanaged (non-.NET) executables, notably including cmd.exe
/ a batch file or powershell.exe
, the Windows PowerShell CLI (even though the latter is built on top of .NET Framework, the .exe
file itself is unmanaged)
does preserve it / does honor -WorkingDirectory
for managed (.NET) executables, notably including pwsh.exe
, the PowerShell (Core) 7+ CLI (though conceivably there are other executables that set their own working dir. on startup).
If you additionally want to preserve the caller's working directory, you must therefore include a cd
command in your cmd.exe
call:
Start-Process cmd.exe "/k cd `"$PWD`" & .\Batch.bat" -Verb RunAs
Note:
Using the automatic $PWD
variable to refer to PowerShell's current directory (location) typically works fine, but won't work in two cases:
If PowerShell's current location isn't a file-system location; e.g., if it is a location on the HKLM:
registry drive.
If the current location is a file-system location that is based on a PowerShell-only drive (established with New-PSDrive
), which the outside world doesn't know about.
If either case applies, use the following instead of $PWD
, which always works:
$((Get-Location -PSProvider FileSystem).ProviderPath)