如何实时pipe理标准输出到批处理脚本

我试图将从程序输出的行输出到另一个脚本,这个脚本可以实时读取行,并根据行中的内容做些什么。

所以例如输出是

$program.exe Program Started Processing info Work Set Started Work Set Ended Results are.. Ending Program 

我想把它传递给一个脚本,当上述程序输出“Work Set Started”时,可以触发另一个程序

我在我的trigger.bat中尝试过以下内容

 @echo off SETLOCAL FOR /F "tokens=1* delims=]" %%A IN ('FIND /N /V ""') DO ( IF "%%B" == "Work Set Started" ( ProgramB.exe ) ) ENDLOCAL 

当我运行以下

 $program.exe | trigger.bat 

ProgramB.exe不是实时启动,但是从trigger.bat收集pipe道的所有输出后执行for循环。

难道我做错了什么? 我必须使用PowerShell吗?

FOR /F命令首先收集执行命令的所有输出,并开始处理每个输出行,直到命令结束。

你可以这样修改你的trigger.bat文件代码:

 @echo off SETLOCAL :nextLine set /P "line=" echo Line received: "%line%" FOR /F "tokens=1* delims=]" %%A IN ("%line%") DO ( IF "%%B" == "Work Set Started" ECHO ProgramB.exe IF "%%B" neq "Ending Program" GOTO NextLine ) 

但是,如果$program.exe的输出速度过快,可能会出现一些同步问题。 我使用这个output.txt文件测试了这个代码:

 [1]$program.exe [2]Program Started [3]Processing info [4]Work Set Started [5]Work Set Ended [6]Results are.. [7]Ending Program 

如果我只是这样做: type output.txt | trigger.bat type output.txt | trigger.bat程序失败,但是如果输出行通过这个output.bat程序慢慢产生:

 @echo off setlocal EnableDelayedExpansion for /F "delims=" %%a in (output.txt) do ( set /A "rand=!random! %% 4 + 2" ping -n !rand! localhost > NUL echo %%a ) 

…然后该方法正常工作:

 C:\> output.bat | trigger.bat Line received: "[1]$program.exe " Line received: "[2]Program Started" Line received: "[3]Processing info" Line received: "[4]Work Set Started" ProgramB.exe Line received: "[5]Work Set Ended" Line received: "[6]Results are.." Line received: "[7]Ending Program" 

所以你需要测试你的具体情况这个方法…

用一个简单的代码来测试生成输出

 @echo off setlocal enableextensions disabledelayedexpansion >"programOutput.txt" ( echo(Program Started echo(Processing info echo(Work Set Started echo(Work Set Ended echo(Results are.. echo(Ending Program ) set program=findstr "^" programOutput.txt %program% | trigger.cmd 

trigger.cmd代码可能是类似的

 @echo off setlocal enableextensions disabledelayedexpansion if "%~1"==".monitor." ( call :monitor ) else ( call :start ) goto :eof :start echo .... Entering monitor for %%t in ("%temp%\%~n0.%random%%random%%random%%random%%random%.tmp") do ( type nul >"%%~ft" findstr "^" > "%%~ft" | ( <"%%~ft" "%~f0" .monitor. & echo Start program B here ) ) & 2>nul del /q "%%~ft" echo .... Main program ended - Leaving monitor goto :eof :monitor rem Try to read data. In case of failure wait and repeat set /p "data=" || ( >nul ping -n 2 "" goto :monitor ) echo(.... monitor has retrieved [%data%] if /i "%data%"=="Work Set Started" ( echo(.... !!! : detected work set start. Leaving monitor goto :eof ) rem No end of monitoring condition found. Try again goto :monitor 

正如Aacini所表明的,

  • for /f的问题是在开始处理之前必须检索所有的数据。
  • 在管道内使用set /p的循环的问题是管道两侧同步。

但是这个问题有一个解决方法。 您可以使用中间文件。 也就是说,您可以将命令的输出重定向到一个文件,并且在程序写入的同时,您可以从该文件读取数据。 不是防弹,但更稳定。

处理它的一个更安全的方法是使用某种方式来指示每行开始的地方。 这是另一个trigger.cmd替代方案,它使用find /n命令来处理监视循环中正在处理的程序的输出。 如果输入缓冲区中的第一个字符是[我们将假设我们已经找到了一行的开始,否则它是一个连续的行。

 @echo off setlocal enableextensions disabledelayedexpansion if "%~1"==".monitor." ( call :monitor ) else ( call :start ) goto :eof :start echo .... Entering monitor set "buffer=" for %%t in ("%temp%\%~n0.%random%%random%%random%%random%%random%.tmp") do ( type nul >"%%~ft" find /n /v "" > "%%~ft" | ( <"%%~ft" "%~f0" .monitor. & echo Start program B here ) ) & 2>nul del /q "%%~ft" echo .... Main program ended - Leaving monitor goto :eof :monitor rem Try to read data. In case of failure wait and repeat set /p "data=" || ( >nul ping -n 2 "" goto :monitor ) echo(.... monitor has retrieved [%data%] rem The output of the process is being numerated by find /n rem so each line is in the form [9]xxxxxxx if not "%data:~0,1%"=="[" ( rem If we don not have a line start, append to previous buffer set "buffer=%buffer%%data%" ) else ( rem New line found to process set "buffer=%data%" ) rem Remove initial [number] data for /f "tokens=2 delims=]" %%a in ("%buffer%") do ( echo(.... checking program output [%%a] if /i "%%a"=="Work Set Started" ( echo(.... !!! detected work set start. Leaving monitor goto :eof ) ) rem No end of monitoring condition found. Try again goto :monitor 

注意 :虽然在写代码的时候还没有意识到,但我重复了(而不是最好的)dbenham的工作。 请参阅这篇文章 ,以获得相同想法的改进版本。