You cannot directly execute batch-file (cmd) commands from PowerShell (which speaks a very different language), but
you can pipe a series of batch-file commands to cmd, the (legacy) Windows command processor, but it has severe limitations:
$commands = @'
@echo off
REM Maintenance Mode on
"D:\vdogServer\VdogMasterService.exe" /at:s /rd:E\vdServerArchive /maintenace :on
if ERRORLEVEL 1 ECHO "NOK" >> d:\MMLOG.txt
if ERRORLEVEL 0 ECHO "OK" >> d:\MMLOG.txt
'@
# !! THIS MAY OR MAY NOT WORK AS INTENDED, DEPENDING ON THE SPECIFIC COMMANDS.
# Simply sends the commands via stdin.
# /q suppresses printing the prompt between commands, and
# /d suppresses autorun entries - see cmd /?
$commands | cmd /q /d
Limitations:
for loops and escaped % chars. do not work,
because, cmd.exe parses commands provided via stdin expecting interactive command-line syntax, not batch-file syntax, which - regrettably, and for historical reasons - differ:
- Batch files must use, e.g.,
%%i as the iterator variable, whereas interactively you must use %i (just one %); e.g., providing a statement such as for /l %%i in (1,1,3) do echo %%i via stdin is a quiet no-op.
- Batch files allow you to escape
% signs as %% (to use them as literals): for instance, you could use %%PATH%% to produce literal %PATH% on output; on the command line - and when piping via stdin - this does NOT work: you end up with %<value of variable>%.
With this invocation style, cmd will not automatically reflect the last command's exit code in its own exit code, and PowerShell's $LASTEXITCODE will therefore NOT reflect failure. (Contrast this with invoking a batch file containing the same commands.)
- Make sure that the code exits with an explicit
exit call to properly set the exit code.
Character-encoding caveat: You need to (temporarily) set $OutputEncoding = [Console]::InputEncoding so as to ensure that batch commands that contain non-ASCII characters are encoded the way cmd.exe expects (that is, based on the active OEM code page).
Finally, there is a cosmetic issue, which, however, would also affect processing the output programmatically:
- Even with
@echo off as the first line, cmd.exe's copyright message invariably prints first (e.g., Microsoft Windows [Version 10.0.19044.1826]...), followed by one instance of the prompt string (e.g. C:\>)
- Either way, each command's source-code line prints before its output.
For these reasons, you're generally better off writing the commands to a (temporary) batch file and invoking that:
Note: You can also use this function to execute the content of a batch file downloaded from the web with Invoke-WebRequest / Invoke-RestMethod, as requested in this related question.
function Invoke-AsBatchFile {
param(
[string] $batchFileContents
)
# Determine a unique file path to serve as a temp. batch file.
$tempBatchFile = "$(Join-Path ([IO.Path]::GetTempPath()) ([IO.Path]::GetRandomFileName())).cmd"
# Write the commands to the batch file.
# Note: -Encoding OEM assumes that the current console window's
# active code page is at its default, the system's active OEM code page.
$batchFileContents | Set-Content -Encoding OEM -LiteralPath $tempBatchFile
# Execute the temp. batch file with pass-through arguments, if any.
# (Reflected in the automatic $args variable.)
& $tempBatchFile $args
# Remove the temp. batch file.
Remove-Item $tempBatchFile
# $LASTEXITCODE now contains the temp. batch file's exit code
# (whereas $? should be ignored).
}
Sample invocation:
$command = @'
@echo off
REM Maintenance Mode on
"D:\vdogServer\VdogMasterService.exe" /at:s /rd:E\vdServerArchive /maintenace :on
if ERRORLEVEL 1 ECHO "NOK" >> d:\MMLOG.txt
if ERRORLEVEL 0 ECHO "OK" >> d:\MMLOG.txt
'@
Invoke-AsBatchFile $command
if ($LASTEXITCODE -ne 0) { Write-Error "Something went wrong." }