1

how can I execute the following example in a PowershellScript?

 @echo off
 REM Maintenance Mode on
 "C:\ProgramFiles\vdogServer\VDogMasterService.exe" /at:s /rd:C:\vdServerArchive /maintenance:on
 if ERRORLEVEL 1 ECHO "versiondog Server wurde nicht ordnungsgemäß in den Wartungsmodus versetzt." >> d:\log.txt
 if ERRORLEVEL 0 ECHO "versiondog Server wurde ordnungsgemäß in den Wartungsmodus versetzt." >> d:\log.txt

I tried that without success:

$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-Expression -Command:$command

Im a Beginner in Powershell yet, would be nice if someone has a solution for that, BR

2
  • 1
    I think the simplest solution would be to put all the batch file code in a batch file and have your powershell script execute the batch file. Commented Oct 5, 2016 at 14:41
  • The best solution would be to rewrite the batch code to PowerShell. Commented Oct 5, 2016 at 14:47

2 Answers 2

3

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." }
Sign up to request clarification or add additional context in comments.

Comments

1

Edited to test exit code per comment:

#Maintenance Mode on 
& "C:\ProgramFiles\vdogServer\VDogMasterService.exe" /at:s /rd:C:\vdServerArchive /maintenance:on
if ($LASTEXITCODE -eq 0) {
    "versiondog Server wurde ordnungsgemäß in den Wartungsmodus versetzt." | out-file d:\log.txt -append
} else {
    "versiondog Server wurde nicht ordnungsgemäß in den Wartungsmodus versetzt." | out-file d:\log.txt -append
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.