1

Hello I have a log file that I am creating and updating every hour, it runs a lot of files therefore it fills up very quickly.

What I want to do is to purge every once in a while only leaving the last 30 lines of the file.

I have been trying to do this for a while I know there is the "More" command that can print the lines from the n line. What I want is to use that command or something similar but instead of printing it I want it to write to a file.

This is what I got so far.

set LINES=0
for /f "delims==" %%I in (test2.txt) do (
    set /a LINES=LINES+1
)
@echo   
set /a LINES=LINES-30
more +%LINES% < test2.txt
Paul
  • 2,620
  • 2
  • 17
  • 27
Yuval Haran
  • 119
  • 1
  • 13

5 Answers5

3

using some simple Logical tricks:

for /f %%i in ('type test2.txt^|find /c /v ""') do set /a lines=%%i-30
if %lines% leq 0 goto :eof
more +%lines% test2.txt>test2.tmp
move /y test2.tmp test2.txt

(this counts also empty lines; Pauls approach ignores them. What's better, depends on your needs)

Stephan
  • 53,940
  • 10
  • 58
  • 91
  • @Stephan Nice solution, I didn't know that I could set the number in that way without incrementing: `set /a lines=%%i-30` +1 and as favorite to remember. – Paul Sep 24 '15 at 09:13
  • Great approach (+1), for not ignoring empty lines! – aschipfl Sep 24 '15 at 09:31
  • Congratulations all on your success reinventing the `tail` command. – lit Sep 24 '15 at 13:13
  • @Paul Thanks ^^ You can make it complete with minor changes: `...('type "%~1"...` and `...do set /a lines=%%i - %2` and saving it as "tail.bat" – Stephan Sep 24 '15 at 13:25
  • Is there a way to do `tail -f`? – lit Sep 26 '15 at 19:08
  • @Stephan - The -f switch does a "follow" on the file. It will continue to output lines as they are added to the file. You might use this to watch the end of a web server log file or anything else that is long running and adds lines to a text file. – lit Sep 27 '15 at 20:42
1

There are probably a simpler solution but here's one that just me immediately in mind.

@echo off

rem creating env testing
(
    for /l %%i in (1,1,50) do (
        echo %%i. What I want to do is to purge every once in a while only leaving the last 30 lines of the file.
    )
)>%TEMP%\_file.tmp

rem Script start here.
set lines=0
for /f "delims=" %%i in (%TEMP%\_file.tmp) do set /a lines+=1

if %lines% LEQ 30 echo nothing todo &exit /b 0

set /a lines=lines-30
set "_skip=skip=%lines%"

set count=0
for /f "%_skip% delims=" %%i in (%TEMP%\_file.tmp) do (
        echo %%i
        set /a count+=1
)
echo found %count% lines
exit /b 0

The trick here is to count the number of line and subtract 30 then you get the number of lines you have to skip.

This is how could be for your need.

@echo off

set lines=0
for /f "delims=" %%i in (test2.txt) do set /a lines+=1

if %lines% LEQ 30 echo nothing todo &exit /b 0

set /a lines=lines-30
set "_skip=skip=%lines%"

(for /f "%_skip% delims=" %%i in (test2.txt) do (
        echo %%i
))>%TEMP%\_file.tmp
move /Y %TEMP%\_file.tmp test2.txt
exit /b 0
Paul
  • 2,620
  • 2
  • 17
  • 27
  • Hi thanks for the answer, it does not work for me though when i run it it prints in cmd window 30 times the line What I want to do is to purge every once in a while only leaving the last 30 lines of the file. and it doesn't update the file test2.txt i see you created and used some sort of temp file but i am not sure how to use that. thank you. – Yuval Haran Sep 24 '15 at 08:19
1

Although @Stephan's answer might be the best approach, I still want to contribute an answer, which relies on the code of the question...

Here is a corrected version of the original code:

@echo off
set LINES=0
setlocal EnableDelayedExpansion
for /F "delims=" %%I in (test2.txt) do (
    set /A "LINES=!LINES!+1"
)
endlocal & set /A "LINES=%LINES%-30"
if %LINES% GEQ 0 more +%LINES% test2.txt

There were two issues:

  1. reading variables like LINES in set /A is more or less equivalent to %LINES%, but you need delayed expansion and explicit !LINES! expansion here;
  2. there was a = too much at the delims= option (you do not want to specify = as delimiter);

However, two issues remain using the for /F implementation:

  1. empty lines are not taken into account by for /F, so such are not counted;
  2. lines beginning with ; are ignored too, because of the default eol= option;

Update

You can avoid delayed expansion if you adapt the syntax of set /A a bit, and you can overcome the issues concerning empty lines and lines starting with ;:

@echo off
set /A "LINES=0"
for /F "delims=" %%I in ('
    findstr /N /R "^" "test2.txt"
') do (
    set /A "LINES+=1"
)
set /A "LINES-=30"
if %LINES% GEQ 0 more +%LINES% "test2.txt"
Community
  • 1
  • 1
aschipfl
  • 33,626
  • 12
  • 54
  • 99
0

Here is a tail.bat that can be used like the traditional tail command. No, still no -fqv capabilities.

@SETLOCAL
@SET EXITCODE=0

@SET /A LINES=10

@IF "%1" EQU "" (
    @ECHO. usage: %0 [-n] filename^(s^)
    @ECHO.
    @ECHO. example: %0 -3 thefile.txt
    @SET EXITCODE=1
    @GOTO :EOF
)

@SET LC=%1
@IF "%LC:~0,1%" EQU "-" (
    @SET /A LINES=%LC:-=%
    @SHIFT
)

@SET FILENAME=%~1
@IF NOT EXIST "%FILENAME%" (
    @ECHO. The file "%FILENAME%" does not exist.
    @SET EXITCODE=2
    @GOTO TheEnd
)

@FOR /F %%i IN ('TYPE "%FILENAME%" ^| FIND /C /V ""') DO @SET /A NLINES=%%i-%LINES%

@IF %NLINES% LEQ 0 (
    @TYPE "%FILENAME%
) ELSE (
    @MORE +%NLINES% "%FILENAME%"
)

:TheEnd
@EXIT /B %EXITCODE%
lit
  • 14,456
  • 10
  • 65
  • 119
0

If you know the machine / OS has PowerShell available then:

@echo off    
PowerShell.exe -Command "Get-Content \".\test2.txt\" -Tail 30" > test2.txt

The performance hit from this will likely be far lower, the other batch methods are dependant on reading and counting the lines in batch-land - not always that good an idea.

Dean Taylor
  • 40,514
  • 3
  • 31
  • 50