2

I am running a batch file in a powershell script.

Powershell script(pw.ps1):

 # Powershell script

$ScriptPath = "C:\Users\Administrator\Documents\bh.bat"

$out = cmd.exe /c "$ScriptPath" 2>&1 | Out-String

$flg = $?

echo "Output: $out"

if ($flg) {
    echo "Worked"
} else {
    echo "Failed"
}

Batch File(bh.bat):

REM Batch File

@echo on

Setlocal EnableDelayedExpansion

schtasks /query /tn T1

if %errorlevel% NEQ 1 (
  echo "Task already scheduled"
) else (
  echo "Task not scheduled"
)

exit /b 0

Output of powershell script:

 PS C:\Users\Administrator\Documents> C:\Users\Administrator\Documents\pw.ps1
Output: 
C:\Users\Administrator\Documents>REM Batch File 

C:\Users\Administrator\Documents>Setlocal EnableDelayedExpansion 

C:\Users\Administrator\Documents>schtasks /query /tn T1 
cmd.exe : ERROR: The system cannot find the path specified.
At C:\Users\Administrator\Documents\pw.ps1:5 char:8
+ $out = cmd.exe /c "$ScriptPath" 2>&1 | Out-String
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (ERROR: The syst...path specified.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError


C:\Users\Administrator\Documents>if 1 NEQ 1 (echo "Task already scheduled" )  else (echo "Task not scheduled" ) 
"Task not scheduled"

C:\Users\Administrator\Documents>exit /b 0 

Failed

PS C:\Users\Administrator\Documents> 

My requirement is as follows:

  1. Powershell script should output "Failed" only if batch script fails with some errors such as syntax or runtime error.
  2. Powershell script should output "Worked" in two cases: task is found or not found. Currently Powershell script gives "Failed" as output if task is not found.

How can I check in the powershell script if the batch file ran successfully?

2

2 Answers 2

2

Change your batch file as follows:

@echo off
setlocal enabledelayedexpansion

schtasks /query /tn T1

:: Analyze the exit code (error level)
:: and map it onto a custom exit code.
:: Note that schtasks doesn't report *specific* error
:: exit codes: 1 means that *something* went wrong,
:: which we assume to be that the task doesn't exist.
if ERRORLEVEL 1 (
  echo Task not scheduled
  set ec=101
) else ( 
  echo Task already scheduled
  set ec=100
)

:: Getting here implies success.
:: Use the custom exit code to report the specific success status.
:: Note: If this batch file is aborted due a syntax error,
::       the caller will see exit code 255. 
exit /b %ec% 

Then invoke it from your PowerShell script as follows:

# Note: 
#  * No need to call via `cmd /c` in this case - you can invoke batch
#    files directly.
#  * Doing so has the added advantage of PowerShell *itself* reporting
#    a statement-terminating error if the batch file cannot b found,
#    which we handle via a try / catch statement.
try {

  $out = & $ScriptPath 2>&1 | Out-String

  # No need for `echo` (alias for `Write-Output`) - implicit output will do.
  "Output: $out"

  # Check the automatic $LASTEXITCODE variable for the exit code from the
  # most recently executed external program.
  # We infer success if we see one of the two custom exit codes.
  $ok = $LASTEXITCODE -in 100, 101

} catch {
  # This only happens if the batch file doesn't exist.
  $ok = $false
  $LASTEXITCODE = 2 # Simulated exit code: File not found.
}

if ($ok) { # task exists or doesn't exist.
  "Worked"
} else {   # something fundamental went wrong
  "Failed with exit code $LASTEXITCODE"
}

Note:

  • While a batch file can have syntax errors - which causes its execution to be aborted and the caller to see exit code 255 - any other error is reported via exit codes (error levels) - and it is up to each command / executable to properly set an exit code, with 0 by convention signaling success, and any nonzero value failure. Not all executables adhere to this convention:

    • Some always report 0, even when error conditions are encountered.
    • Some only ever report two exit codes: 0 for success, and 1 for any error - schtasks.exe falls into this category.
    • Some communicate specific outcomes with specific exit codes, allowing you to detect specific (error conditions).
    • Some, notably robocopy.exe, even use (of necessity) nonzero exit codes to communicate specific success states - this technique is also used in the code above.
  • If the only thing you need to know is whether the batch file exited abnormally, due to a syntax error, you can make do without the custom exit code and simply use
    $ok = $LASTEXITCODE -ne 255

  • Since your batch file sets a specific exit code explicitly with exit /b <n>, direct invocation from PowerShell (with &, the call operator) works fine, but note that
    cmd.exe /c call <batch-file> - note the call - is generally the most robust way to invoke a batch file - see this answer.

Sign up to request clarification or add additional context in comments.

1 Comment

@AbhayGupta, please see my update. I've also restored 2>&1 | Out-String (I misunderstood the original intent).
0

I solved the issue by changing the batch file(bh.bat) as follows:

REM Batch File

@echo on

Setlocal EnableDelayedExpansion

schtasks /query /fo LIST | findstr TaskName | findstr T1 > nul

if %errorlevel% NEQ 1 (
  echo "Task already scheduled"
) else (
  echo "Task not scheduled"
)

exit /b 0

The powershell script remains the same.

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.