execute batch files in parallel and get exit code

2019-09-15 18:28发布

How can I execute set of batch files from single batch file in parallel and get the exit code from each. When I use start it executes the batch file in parallel (new cmd window) but don't return the exit code from each. And while using call, I can get the exit code but the batch file execution happens sequentially. I have following code:

ECHO ON
setlocal EnableDelayedExpansion
sqlcmd -S server_name -E  -i select_code.sql  >\path\output.txt

for /f "skip=2 tokens=1-3 delims= " %%a in ('findstr /v /c:"-"  \path\output.txt') do (

echo $$src=%%a>\path1\%%c.prm
echo $$trg=%%b>>\path1\%%c.prm
set param_name=%%c

start cmd \k  \path\exec_pmcmd_ctrm.bat workflow_name  %%param_name%%
ping 1.1.1.1 -n 1 -w 5000 > nul
set exitcode="%V_EXITCODE%"
echo %exitcode%>>\path\exitcode.txt
)   

This executes the exec_pmcmd_ctrm.bat 3 times with different variable in parallel , but I am unable to get the exit code from each execution. I tried using call but then I miss the parallel execution of bat file. Any help in this regard?

2条回答
老娘就宠你
2楼-- · 2019-09-15 18:39

Here is a pure solution. Basically, this script executes all batch files located in the same directory simultaneously, where the exit code (ErrorLevel) of each one is written to an individual log file (with the same name as the batch file and extension .log); these files are checked for existence; as soon as such a log file is found, the stored exit code is read and copied into a summary log file, together with the respective batch file name; as soon as all log files have been processed, this script is terminated; the exit code of this script is zero only if all the exit codes of the executed batch files are zero too. So here is the code -- see all the explanatory rem remarks:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Collect available scripts in an array:
set /A "INDEX=0"
for %%J in ("%~dp0*.bat" "%~dp0*.cmd") do (
    if /I not "%%~nxJ"=="%~nx0" (
        set "ITEM=%%~fJ"
        call set "$BATCH[%%INDEX%%]=%%ITEM%%"
        set /A "INDEX+=1"
    )
)

rem // Execute scripts simultaneously, write exit codes to individual log files:
for /F "tokens=1,* delims==" %%I in ('set $BATCH[') do (
    start "" /MIN cmd /C rem/ ^& "%%~fJ" ^& ^> "%%~dpnJ.log" call echo %%^^ErrorLevel%%
)

rem // Deplete summary log file:
> "%~dpn0.log" rem/

rem // Polling loop to check whether individual log files are available:
:POLLING
rem // Give processor some idle time:
> nul timeout /T 1 /NOBREAK
rem /* Loop through all available array elements; for every accomplished script,
rem    so its log file is availabe, the related array element becomes deleted,
rem    so finally, there should not be any more array elements defined: */
for /F "tokens=1,* delims==" %%I in ('set $BATCH[') do (
    rem // Suppress error message in case log file is not yet available:
    2> nul (
        rem // Read exid code from log file:
        set "ERRLEV=" & set "FILE="
        < "%%~dpnJ.log" set /P ERRLEV=""
        if defined ERRLEV (
            rem // Copy the read exit code to the summary log file:
            set "NAME=%%~nxJ"
            >> "%~dpn0.log" call echo(%%ERRLEV%%    "%%NAME%%"
            rem // Undefine related array element:
            set "%%I="
            rem // Store log file path for later deletion:
            set "FILE=%%~dpnJ.log"
        )
        rem // Delete individual log file finally:
        if defined FILE call del "%%FILE%%"
    )
)
rem // Jump to polling loop in case there are still array elements:
> nul 2>&1 set $BATCH[ && goto :POLLING

rem // Check individual exit codes and return first non-zero value, if any:
set "ERRALL="
for /F "usebackq" %%I in ("%~dpn0.log") do (
    if not defined ERRALL if %%I neq 0 set "ERRALL=%%I"
)
if not defined ERRALL set "ERRALL=0"

endlocal & exit /B %ERRALL%

This approach is quite similar to the one I used in the following Improving Batch File for loop with start subcommand, but there the outputs of simultaneously executed commands are collected.

查看更多
啃猪蹄的小仙女
3楼-- · 2019-09-15 18:53

First of all, the "exit code" from another Batch file (that ends with exit /B value command) is taken via %ERRORLEVEL% variable (not %V_EXITCODE%). Also, if such a value changes inside a FOR loop, it must be taken via Delayed Expansion instead: !ERRORLEVEL! (and EnableDelayedExpansion at beginning of your program). However, these points don't solve your problem because there is a misconception here...

When START command is used (without the /WAIT switch), a parallel cmd.exe process start execution. This means that there is not a direct way that the first Batch file could know in which moment the parallel Batch ends in order to get its ERRORLEVEL at that point! There is not a "wait for a started Batch file" command, but even if it would exist, it don't solve the problem of have several concurrent Batch files. In other words, your problem can not be solved via direct commands, so a work around is necessary.

The simplest solution is that the parallel Batch files store their ERRORLEVEL values in a file that could be later read by the original Batch file. Doing that imply a synchronization problem in order to avoid simultaneous write access to the same file, but that is another story...

查看更多
登录 后发表回答