Display output of batch file on the screen and log file?

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
Aacini
Expert
Posts: 1913
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Display output of batch file on the screen and log file?

#16 Post by Aacini » 09 Feb 2013 20:43

I said in my previous reply that it is very easy to write a Tee feature in Batch using a pipeline, but it have synchronization problems caused by SET /P command that does not correctly behave when it read from redirected Stdin. When SET /P read from keyboard it waits until the user press Enter, but when it read from a file or redirected stream it immediately return if there is no data ready, so it treat the same way an End Of File condition than a stream buffer empty because the piped process has not sent more data. To solve this problem we must synchronize the input/output operations at both sides of the pipe, and the easiest way to do that is with a semaphore system implemented via the presence of a disk file.

The synchronization mechanism implemented in the Batch file below works on line-by-line basis. If you want to pass through the pipe the multi-line output of a command, it must be splitted in individual lines that must be processed by the semaphore system. Also, a conventional value is needed in order to detect the real End Of input Stream.

Code: Select all

@echo off
setlocal EnableDelayedExpansion

rem Synchronization of concurrent input/output pipe operations via a semaphore system
rem Example of TEE feature for a particular program
rem Antonio Perez Ayala

rem Define auxiliary variable for nested CALLs of this Batch file
set CALL="%~F0"

rem Nested Batch file call dispatcher
if "%~1" equ ":Source" goto Source
if "%~1" equ ":Tee" goto Tee

echo The text between the lines will be "TEE'd" into TeeOutput.txt file
echo ------------------------------------------------------------------

rem Create the semaphore-signal file
echo X > Flag.out
del Flag.in 2> NUL

rem Execute the Source of text and pipe it into the TEE subroutine
del TeeOutput.txt 2> NUL
%CALL% :Source | %CALL% :Tee TeeOutput.txt

echo ------------------------------------------------------------------
echo End of TEE example
goto :EOF


:Source
setlocal DisableDelayedExpansion
call :SendData Today is %date% @ %time%
call :SendData Listing of "DIR *.BAT" command:
call :SendData
for /F "tokens=1* delims=:" %%a in ('dir *.bat ^| findstr /N "^"') do (
   call :SendData "%%b"
)
call :SendData
call :SendData End of data duplicated by TEE
rem  Send the special data that indicate "End Of piped File"
call :SendData :EOF
exit /B


rem Each data sent is followed by "data available" signal
rem and then waits for "data read" acknowledgement

:SendData data
echo(%*
rem Set "data available" signal
ren Flag.out Flag.in
   :WaitDataAcknow
   if not exist Flag.out goto WaitDataAcknow
exit /B


:Tee file
   rem Wait for "data available" signal
   if not exist Flag.in goto Tee
   rem Read the input line
   set line=
   set /P line=
   rem Set "data read" acknowledgement
   ren Flag.in Flag.out
   rem Check for special "End Of piped File" data
   if "!line!" equ ":EOF" exit /B
   rem Duplicate input line in Stdout and into the parameter file
   set "enclosingQuotes=!line:~0,1!!line:~-1!"
   if !enclosingQuotes! equ "" set "line=!line:~1,-1!"
   echo(!line!
   echo(!line!>> %2
goto Tee

Previous mechanism may also be used in other piped scenarios, not just in Tee command.

Although it is possible to implement a similar mechanism via a FOR /F command that get input lines from a process and duplicate they in Stdout and a tee file, this mechanism is synchronous, that is, the output will appear in the screen until the process that generate it ends. On the other hand, the mechanism shown above is simultaneous (asynchronous) so screen output will appear as soon as the output is generated.

Antonio

Aacini
Expert
Posts: 1913
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Display output of batch file on the screen and log file?

#17 Post by Aacini » 18 May 2013 03:36

I developed a new asynchronous Tee program that does not require the collaboration of the target process as the previous version does, so it correctly works with any process. It uses a JScript code section to get the data from process Stdout, but the processing of the data is done in the Batch section. This approach allows any Batch programmer to modify the program in order to suit the code to specific needs. As an example of this feature, the version below correctly process CLS commands as the OP requested.

Code: Select all

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


@echo off
setlocal EnableDelayedExpansion

rem APATee.bat: Asynchronous (real time) Tee program, Batch-JScript hybrid version
rem Antonio Perez Ayala

rem The advantage of this program is that the data management is written in Batch code,
rem so any Batch programmer may modify it to fit their own needs.
rem As an example of this feature, CLS command is correctly managed

if "%~1" equ "" (
   echo Duplicate the Stdout output of a command in the screen and a disk file
   echo/
   echo anyCommand ^| APATee teeFile.txt [/A]
   echo/
   echo If /A switch is given, anyCommand output is *appended* to teeFile.txt
   goto :EOF
)

if "%2" equ ":TeeProcess" goto TeeProcess
   
rem Get the output of CLS command
for /F %%a in ('cls') do set "cls=%%a"

rem If /A switch is not provided, delete the file that receives Tee output
if /I "%~2" neq "/A" if exist %1 del %1

rem Create the semaphore-signal file and start the asynchronous Tee process
echo X > Flag.out
if exist Flag.in del Flag.in
Cscript //nologo //E:JScript "%~F0" | "%~F0" %1 :TeeProcess
del Flag.out
goto :EOF

:TeeProcess
   rem Wait for "Data Available" signal
   if not exist Flag.in goto TeeProcess
   rem Read the input line sent by JScript code
   set line=
   set /P line=
   rem Set "Data Read" acknowledgement
   ren Flag.in Flag.out
   rem Check for the standard "End Of piped File" mark
   if "!line!" equ ":_EOF_:" exit /B
   rem Correctly manage CLS command
   if "!line:~0,1!" equ "!cls!" (
      cls
      set "line=!line:~1!"
   )
   rem Duplicate input line in Stdout and the Tee output file
   echo(!line!
   echo(!line!>> %1
goto TeeProcess


@end


// JScript section

var fso = new ActiveXObject("Scripting.FileSystemObject");
// Process all lines of Stdin
while ( ! WScript.Stdin.AtEndOfStream ) {
   // Read the next line from Stdin
   var line = WScript.Stdin.ReadLine();
   // Wait for "Data Read" acknowledgement
   while ( ! fso.FileExists("Flag.out") ) {
      WScript.Sleep(10);
   }
   // Send the line to Batch code
   WScript.Stdout.WriteLine(line);
   // Set "Data Available" signal
   fso.MoveFile("Flag.out", "Flag.in");
}
// Wait for last "Data Read" acknowledgement
while ( ! fso.FileExists("Flag.out") ) {
      WScript.Sleep(10);
}
// Send the standard "End Of piped File" mark
WScript.Stdout.WriteLine(":_EOF_:");
fso.MoveFile("Flag.out", "Flag.in");

This is a small Batch program that may be used to test APATee.bat:

Code: Select all

@echo off
setlocal
set count=0
:again
   echo The screen will be cleared in 5 seconds
   set hora=%time%
   echo %hora%
   set wait=0
   :wait
      if "%hora:~0,8%" equ "%time:~0,8%" goto wait
      set hora=%time%
      echo %hora%
      set /A wait+=1
   if %wait% lss 5 goto wait
   cls
   set /A count+=1
if %count% lss 3 goto again
echo End of test

For example:

Code: Select all

test.bat | APATee.bat test.txt

Antonio

Post Reply