使用并发stdout输出bat多个`start`命令

时间:2015-02-05 15:36:30

标签: batch-file concurrency stdout

我有一个批处理脚本,它使用start命令启动多个其他程序(同一个的多个实例)来并行化任务,因为它们是独立的。

然而,这些命令会将一些状态消息输出到stdout,这些消息完全搞砸了。有没有办法确保

  • a)至少打印每一行而不中断输出 其他任务 - 或 -
  • b)首先收集过程的输出并在完成后同步打印? - 或
  • c)我尚未想到的另一种方式。

2 个答案:

答案 0 :(得分:1)

您可以将每个命令的输出重定向到自己的文本文件,在最终命令上使用start /wait,然后读取所有文本文件。

@echo off
setlocal

for /L %%I in (1,1,5) do (
    start "" "powershell" -command "(Get-Date) -f ''" ^>output%%I.txt
)
start /wait "" "powershell" -command "(Get-Date) -f ''" ^>output6.txt

for /L %%I in (1,1,6) do (
    type output%%I.txt
    del output%%I.txt
)

vlad_tepesch评论说:
  

最后一个启动的程序不一定是退出的最后一个程序

修改

vlad_tepesch评论说:
  

...你提到的"任务列表"给了我一些想法:监控任务列表并等到启动的任务消失。

好主意。试试这个:

@echo off
setlocal

for /L %%I in (1,1,6) do (
    start "" "powershell" -command "(Get-Date) -f ''" ^>output%%I.txt
)

:wait
tasklist | find /i "powershell" >NUL && goto wait

for /L %%I in (1,1,6) do (
    type output%%I.txt
    del output%%I.txt
)

这只是一个概念证明。如果您要生成不同的命令,只需替换

tasklist | find /i "powershell" >NUL && goto wait

tasklist | findstr /i "program1 program2 program3 program4 etc" >NUL && goto wait

...将每个衍生的可执行文件的名称插入findstr


旧的JScript解决方案:

Windows Scripting Host(VBScript或JScript)可以更好地处理异步进程的更高级管理。

好吧,我想如果你不需要这些应用程序'控制台输出,然后您可以调用wmic process call create 'command'并使用for /f循环捕获PID,等待PID从任务列表中消失。这是通过纯批次实现目标的一种方式。但是没有优雅的方式来捕获command的输出。

相反,使用JScript对象以WshShell.Exec收集生成的进程会更好。然后你可以按照你喜欢的方式处理程序输出 - 如果你愿意,可以将它们写入控制台和日志文件,或者只写一个或另一个。

@if (@CodeSection == @Batch) @then

@echo off
setlocal

rem // push list of tasks into jobs file
>"jobs.txt" (
    echo wmic nicconfig where "dhcpserver like '%%.%%'" get macaddress ^| find ":"
    echo ping -n 3 localhost
    echo ipconfig
)

rem // process jobs file with JScript
cscript /nologo /e:JScript "%~f0" "jobs.txt"
del "jobs.txt"

echo;
echo Done.  That was ossum!

rem // End main runtime
goto :EOF
@end
// begin JScript chimera

var fso = WSH.CreateObject('Scripting.FileSystemObject'),
    osh = WSH.CreateObject('Wscript.Shell'),
    jobsfile = fso.OpenTextFile(WSH.Arguments(0), 1),
    jobs = jobsfile.ReadAll().split(/\r?\n/),
    procs = {};

jobsfile.Close();

for (var i=0, end=jobs.length; i<end; i++) {
    procs[i] = osh.Exec('cmd /c ' + jobs[i]);
    WSH.Echo('PID ' + procs[i].ProcessID + ': ' + jobs[i]);
}
WSH.Echo('Working...\n');

// wait for all jobs to complete
while (1) {
    var complete = 0;
    for (var i in procs) if (procs[i].Status) complete++;
    if (complete == jobs.length) break;
    WSH.Sleep(1);
}

for (var i in procs) {
    if (!procs[i].StdOut.AtEndOfStream) WSH.StdOut.Write(procs[i].StdOut.ReadAll());
    if (!procs[i].StdErr.AtEndOfStream) WSH.StdErr.Write(procs[i].StdErr.ReadAll());
}

答案 1 :(得分:1)

这是一个纯粹的批处理解决方案,可以打印每个并行流程的完整行:

@echo off
setlocal EnableDelayedExpansion

rem If this program was re-executed to arbitrate output, do it
if "%~1" equ ":ArbitrateOutput" goto %1

rem Execute N parallel instances of other program
for /L %%i in (1,1,4) do (
   start /B "" "cmd /C otherProgram %%i  |  "%~F0" :ArbitrateOutput 2> NUL"
)
goto :EOF


rem Arbitrates concurrent output to screen from parallel processes
:ArbitrateOutput

rem Read a line from our companion parallel process
set "line=:EOF"
set /P "line="
if "%line%" equ ":EOF" exit

rem Try: to set an exclusive lock for unique output
:uniqueOutput
2> lock (
   rem Send the line to the screen and delete it
   echo %line%
   set "line="
)

rem Catch: if the line was not sent, try again
if defined line goto uniqueOutput

goto :ArbitrateOutput

例如,使用此otherProgram.bat

@echo off

rem Get a random number of messages
set /A messages=%random% %% (%time:~-1%+1) + 1
ECHO INSTANCE %1: MESSAGES %MESSAGES%

for /L %%i in (1,1,%messages%) do (
   ping -n 2 localhost > NUL
   echo Greetings from instance number %1
)
exit

......这是输出:

C:\> test

C:\> INSTANCE 4: MESSAGES 3
INSTANCE 3: MESSAGES 4
INSTANCE 1: MESSAGES 2
INSTANCE 2: MESSAGES 5
Greetings from instance number 2
Greetings from instance number 1
Greetings from instance number 3
Greetings from instance number 4
Greetings from instance number 3
Greetings from instance number 2
Greetings from instance number 4
Greetings from instance number 1
Greetings from instance number 3
Greetings from instance number 2
Greetings from instance number 4
Greetings from instance number 3
Greetings from instance number 2
Greetings from instance number 2

如果并行进程在同一操作中发送多行,则此方法可能会丢失一些数据,也就是说,对于以失效分隔的单行,它可以更好地工作。此外,它在进程的第一个空行输出处停止。如果需要,可以修复最后一点。