simple Batch based Tail

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
arjunae

simple Batch based Tail

#1 Post by arjunae » 14 Dec 2022 20:37

@echo off
chcp 65001
Mode 151,49
REM A simple Tail like pure batch script
REM Set initial default Lines to print
set linesbefore=1024
set follow=yes
if "%~1"=="" echo Usage: tail -n lines filename & goto :eof
if "%~1" equ "-n" (set follow=no& set "linesbefore=%2" & set filename=%~3) else (set filename=%~1)
if "%~1" equ "-nl" (REG add HKCU\Console\ /v taillock /t REG_BINARY /d 00000000 /f >nul & goto :eof)
setlocal enabledelayedexpansion enableextensions

REM
REM Iterate through and print newly added Lines.
set LineBufferNr=0
set LineBufferArray=x
set prevFileLineNr=0

REM Check the tail entries value or set it if appropriate
REG query HKCU\Console\ /v taillock 1>Nul 2>Nul
if "%errorlevel%" equ "0" for /F "delims=" %%j in ('REG query HKCU\Console\ /v taillock 2^>NUL') DO (set Value=%%j)
if "!value!" EQU " taillock REG_BINARY 00000001" echo Info HKCU\Console\taillock has been found. & Pause
where %filename% 1>NUL & IF !errorlevel! GTR 0 (echo theres no File %FileName%) & goto :eof

:think
REG add HKCU\Console\ /v taillock /t REG_BINARY /d 00000001 /f >nul
REM for /F "tokens=2 delims=:" %n in ('find /V /C "" "%filename%"') do Set /a FileLines=%n
REM store newly added Lines in the array and print them
set /a FileLineNr=0
FOR /f "tokens=*" %%b IN ('type %filename%') DO (
set /a FileLineNr+=1
if "%%b" neq "" set /a LineBufferNr+=1 & set LineBufferArray!LineBufferNr!=%%b
if !FileLineNr! equ !prevFileLineNr! (set /a skipLines=!LineBufferNr!+1)
)
if !FileLineNr! gtr !prevFileLineNr! (
set /a newLines =!FileLineNr!-!prevFileLineNr!
REM echo Debug: Recieved !newLines! new lines
REM initially print Files lines up to the length of the lineBuffer
if !LinesBefore! gtr %LineBufferNr% set /a LinesBefore=!LineBufferNr!
if !LinesBefore! gtr 0 set /a LinesBefore=%LineBufferNr%-!LinesBefore!+1 & set /a skipLines=!LinesBefore!
for /L %%o in (!skipLines!,1,!LineBufferNr!) do (echo !LineBufferArray%%o!)
set LinesBefore=0
set prevFileLineNr=%FileLineNr%
)

:wait
set prevFileLineNr=!FileLineNr!
set LineBufferNr=1
REM play or Debate internally for some time.
for /l %%i in (1,1,200) do (
echo debate internally and decide friendly democratic and open with everyone involved >NUL
)
if "%follow%" equ "yes" goto think else goto en

:en
REG add HKCU\Console\ /v taillock /t REG_BINARY /d 00000000 /f >nul
Last edited by arjunae on 25 Feb 2023 20:34, edited 27 times in total.

arjunae

Re: simple Batch based Tail

#2 Post by arjunae » 15 Dec 2022 14:03

Note: automatic syncing within batch files between the forked process and the streaming tail.bat requires a helper batch, because the sync statement (registry entry) needs to be called in control flow 2 after the nmake cmd. Edit it might be possible to do that in a one liner but i couldnt find something - at least for that special case here.

REM The next cmds will fork the nmake process and pipes its output to the log which is followed by the tail script.
REM when nmake has finished, it sends a signal to the tail batch which returns control to the calling batch flow.
REM Edit to keep it simple, we write a temporary batch file to accomplish that.

Code: Select all

build.bat contents

@echo off
setlocal enabledelayedexpansion enableextensions
REM clear logfile, remember current location
echo Start Log File >%tmp%\tailtmp
pushd %cd%

:vsregsearch
for /F %%i in ('reg query HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\  /s /f "Visual Studio Build Tools"^|findstr "install"') DO (set installerPath=%%i ) 
if "!installerPath!" equ "" goto vsfilesearch
for /F "delims=" %%j in ('reg query !installerPath! /s /f "InstallLocation"^|findstr "Build"') DO (set rawString="%%j" )
REM retrieve the Path from the registry entries String and check if its valid.
SET vsPath=!rawString:*    InstallLocation    REG_SZ    =! & cd !vsPath! 
Echo calling BuildTools from registry entry !vsPath!
if %errorlevel% EQU 1 (goto vsfileSearch) else call VC\Auxiliary\Build\vcvarsall.bat %arch%

REM Optionally do a filesearch for vcvarsall.bat in %PATH% and program files x64 / x86. (compatible with older versions, but slower)  
:vsfilesearch
Echo Searching vcvarsall.bat in Path, %ProgramFiles% and %ProgramFiles(x86)%
FOR /F "tokens=*" %%i IN ('where vcvarsall.bat 2^>NUL' ) DO echo %%i %arch% & call "%%i" %arch%
if /i "!WindowsSdkDir!"==""  FOR /F "tokens=*" %%i IN ('where /r "%ProgramFiles%"\ vcvarsall.bat 2^>NUL' ) DO echo %%i %arch% & call "%%i" %arch% 
if /i "!WindowsSdkDir!"==""  FOR /F "tokens=*" %%i IN ('where /r "%ProgramFiles(x86)%"\ vcvarsall.bat 2^>NUL'  ) DO echo %%i %arch% & call "%%i" %arch% 
if /i "!WindowsSdkDir!"=="" goto :eof

echo 1: Main Control Flow, Compiling Scintilla source
echo 1: Because nmake wont spill build warnings to both console and a log file, we start it as a fork, then return to the main flow which reads back the log file created by the forked task to control flow 1 stdout while its created.
echo.

start /b cmd /e:on /f:on /c helper.bat
call tail.bat %tmp%\tailtmp && echo .>nul
echo 1: main control flow resumed. 

helper.bat Contents 
@echo off
REM check for and write a sticky note at the bee hives whiteboard so other bees know that a group alreadys on the task.
REG query HKCU\Console\ /v taillock >Nul
if "%errorlevel%" equ "1" choice /c yn Tail already seems to run. continue ?
if "%errorlevel%" equ "2" set errorlevel=1 & goto en
REG add HKCU\Console\ /v taillock /t REG_BINARY /d 00000001 /f
echo 2: nmakes control flow initiating... 
cd src\scintilla\win32
nmake -f scintilla.mak > C:\Users\Thinkpad\AppData\Local\Temp\tailtmp

echo 2: signalling back that nmakes control flow has finished... 
:en
REM remove the sticky Note and put it where sticky notes want to be when their currently not used.
REG delete HKCU\Console\ /v taillock /f
Last edited by arjunae on 25 Feb 2023 20:38, edited 3 times in total.

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

Re: simple Batch based Tail

#3 Post by Aacini » 16 Dec 2022 23:53

Mmmm... Ok, these are my thoughts

At the very first place, your Batch file is not a Tail one! It always show all file lines.

I think your Batch file would lost lines if the file is small and the forked process output lines very fast. Also, your Batch file lost any "duplicated line", that is, any line that is equal to the previous one...

I can't resist the temptation and wrote my own tail.bat file. Here it is:

Code: Select all

@echo off

rem A simple Tail in pure Batch
rem Antonio Perez Ayala

if "%~1" neq "" if "%~1" neq "/?" goto begin

:usage
echo Usage: tail [-n num] [-f] file
echo/
echo    -n   Show the last 'num' lines of the file. Default: 10
echo    -f   Follow the growth of the file
goto :EOF

:begin
setlocal EnableDelayedExpansion
set "lines=10"
set "follow="
if "%~1" equ "-n" (
   set "lines=%2"
   shift & shift
)
if "%~1" equ "-f" (
   set "follow=1"
   shift
)
set "file=%~1"
if not defined file goto usage
if not exist "%file%" echo File not found & goto :EOF
for /F "tokens=2 delims=:" %%n in ('find /V /C "" "%file%"') do set /A "skip=%%n-lines, skip*=(skip>>31)+1"
call :tail < "%file%"
goto :EOF


:tail
setlocal EnableDelayedExpansion

rem Skip first %skip% lines
for /L %%i in (1,1,%skip%) do set /P "="
rem Show last %lines% lines
for /L %%i in (1,1,%lines%) do (
   set "line="
   set /P "line="
   echo(!line!
)
if not defined follow exit /B

:follow
   set "line="
   set /P "line="
   if defined line echo(!line!
   waitFor /T 1 StopTail > NUL 2>&1
if errorlevel 1 goto follow
exit /B
The method to stop the Follow cycle in this program is sending a StopTail signal via waifor command, that is:

Code: Select all

waitfor /SI StopTail
The same waitfor command used to break the Follow cycle allows you to minimize CPU usage in exchange for displaying one output line per second. If the forked process takes too much time between lines, you may increase the wait time in /T switch. If you want to show output lines faster than one per second you have to revert to the LockFile method, but this method waste a lot of CPU time.

I don't understand why you say that the "automatic syncing within batch files between the forked process and the streaming tail.bat requires a helper batch file". You can start the forked process (including the sync waitfor /SI StopTail command) and the streaming tail.bat file in the same Batch file. For example:

Code: Select all

echo Compiling Scintilla source
cd "src\scintilla\win32"
start /B cmd /C nmake -f scintilla.mak ^> %tmp%\scitelog  ^&  waitfor /SI StopTail

REM Call Tail, wait for it returning the flow.
call tail.bat %tmp%\scitelog && echo .
Antonio

arjunae

Re: simple Batch based Tail

#4 Post by arjunae » 17 Dec 2022 21:34

thank you for clearing things up and your code Antonio! i didnt expect dos for having a waitfor which wont stress io. (like the filesize check loop aproach)
its like https://twitter.com/OTerrifying/status/ ... 3478165505 and might even function with faster apache logs.
using the loop didnt fail because build logs a rather slow streams eg:
https://twitter.com/buitengebieden/stat ... 7523865600
remember its a short alpha not specially written to replace anything existing. All it does is writing the last line of the given input file.

experimenting showed, that the lockfile removing statement wasnt called by the interpreter when written within single quotes behind the redirection operator. start cmd -c 'cd directory & nmake xyt -f makefile ^>log & del lock'
it was necesary to call it after nmake finishes, so that the control flow of the batch could continue.
(REM the cd was requirement from nmake or the linker called by it because it wont find the the makefile even with the transferred environment) the del had to be executed after nmake finishes.

Ill play with it on sunday -have a nice Weekend ! 🛠 😀

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

Re: simple Batch based Tail

#5 Post by Aacini » 18 Dec 2022 19:47

arjunae wrote:
17 Dec 2022 21:34
. . .

experimenting showed, that the lockfile removing statement wasnt called by the interpreter when written within single quotes behind the redirection operator. start cmd -c 'cd directory & nmake xyt -f makefile ^>log & del lock'
it was necesary to call it after nmake finishes, so that the control flow of the batch could continue.

the del had to be executed after nmake finishes.
I am afraid I don't follow you... Please, complete this test:

Code: Select all

start /B cmd /C echo Start ^& timeout 10 ^& echo End
In this example the timeout command is executed after the echo Start finished, and echo End command is executed after timeout command finished.

If you enter this line:

Code: Select all

start /B cmd /C cd directory ^& nmake xyt -f makefile ^>log ^& del lock
... then cd is executed first, then the nmake and after it finished, the del is executed.

What happen if you insert not required apostrophes in the line or omit the required caret before & signs? Well, probably the line don't works but, why you would want to complete such experiments? I suppose you want to solve your problem, not investigate what would happen if you modify the required syntax in several ways...

Antonio

arjunae

Re: simple Batch based Tail

#6 Post by arjunae » 18 Dec 2022 23:53

hi, that accomplishes execing del but unfortunately it seems this wont get the 2 control flows correcty, like it does when written as a two liner. but as it's okay as written before there's no need to somehow squeeze it into one line and such not worth a bigger discussion. Ill update the code to use waitfor and try both for the build log tail in the next days.

thank you for helping me !
Tho

Code: Select all

call "c:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x86 >NUL
echo control flow 1 
start /B cmd /C cd src\scintilla\win32 ^& nmake -f scintilla.mak ^>%tmp%\scitelog ^& del %tmp%\lock
echo control flow 1 
pause
C:\Users\Thinkpad\Desktop\NeuerOrdner\myScite\src.3.8.0.lua53>call "c:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x86 1>NUL
control flow 1
control flow 1
Drücken Sie eine beliebige Taste . . .
Microsoft (R) Program Maintenance Utility, Version 14.34.31937.0
Copyright (C) Microsoft Corporation. Alle Rechte vorbehalten.

arjunae

Re: simple Batch based Tail

#7 Post by arjunae » 22 Dec 2022 14:18

Hi - Bugs are fixed now, tested with for /l %a in (1,1,10) do echo Line%a >> someLog.log ...
Ps I really hate Putins war. it has to end somwhen.
Anyway- thanks for the help again. Enjoy your Day!
Thorsten

Post Reply