我有一个批处理脚本,它使用start
命令启动多个其他程序(同一程序的多个实例)来并行化任务,因为它们是独立的。
然而,这些命令会向stdout
输出一些状态消息,这些消息完全被搞砸了。有什么方法可以确保
- a) 在不中断其他任务-或-
- b) 流程的输出首先被收集,并在完成后同步打印?-或
- c) 我还没有想过其他的方式
您可以将每个命令的输出重定向到其自己的文本文件,在最终命令上使用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脚本主机(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());
}
这是一个纯批量解决方案,可以打印每个并行过程的完整行:
@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
如果一个并行进程在同一操作中发送多条线,这种方法可能会丢失一些数据,也就是说,它对间隔一段时间的单行更有效。此外,它在进程的第一个空行输出处停止。如果需要,可以固定最后一点。