rename a file removing part of the filename batch script - batch-file

I have some files in the form:
filename1 1 extra1.ext
filename1 2.ext
filename1 3 extra2.ext
...
filename2 1.ext
filename2 100 extra3.ext
...
filename20 1.ext
filename20 15 extra100.ext
(etc.)
...where filename1, filename2, etc., can contain spaces, symbol ' but not numbers. And extra1, extra2, etc, can contain anything. The number in the file name enclosed by spaces does not repeat per same filename1, filename2, etc.
What i want is to remove the extra things of the files that contain it. That is, to get from filename20 15 extra100.ext to filename20 15.ext
My first attempt is this:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "FILE=file name 11 con sosas extras 2.txt"
set "ext=txt"
set "folder=."
for /F "tokens=1,* delims=0123456789" %%A in ("!FILE!") do (set "EXTRA=%%B")
set "FIRST=!FILE:%EXTRA%=!"
set "filename=!FIRST!.!ext!"
echo !EXTRA!
echo !filename!
echo rename "!folder!\!FILE!" "!filename!"
that seems to work, but if i change it to receive parameters, it doesn't:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "FILE=%1"
set "ext=%2"
set "folder=%3"
for /F "tokens=1,* delims=0123456789" %%A in ("!FILE!") do (set "EXTRA=%%B")
set "FIRST=!FILE:%EXTRA%=!"
set "filename=!FIRST!.!ext!"
echo !EXTRA!
echo !filename!
echo rename "!folder!\!FILE!" "!filename!"
where %1 is the filename, %2 is the extension and %3 is the folder in which the files are. Probably, the extension can be extracted inside the batch, but i don't know how to do it.
On another hand, i plan to use this batch into another one. There, there will be a for loop in (*.txt) and i don't know how to differentiate between files that have extra things (and then call this batch) from files that doesn't (and then not call this batch).
Regards,

use your method to extract the "extra-portion". In a second step, remove that extra-portion:
#echo off
setlocal enabledelayedexpansion
set "FILE=file name 11 con sosas extras 2.txt"
for /f "tokens=1,* delims=1234567890" %%a in ("%file%") do set new=!file:%%b=!%%~xb
echo %new%
%%~xb gives you the extension.

Here is a batch script that seeks the first purely numeric string portion enclosed within SPACEs, or in case it appears at the end, preceded by a SPACE, that occurs after some other text not consisting of SPACEs only. The part in front of the found number followed by a SPACE followed by the number itself are used for building the new file name.
This approach handles all valid characters for file names properly, even ^, &, %, !, ( and ).
So here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=.\test"
for /F "eol=| delims=" %%F in ('
dir /B "%_SOURCE%\*.ext" ^| findstr /R /I ^
/C:"^..* [0123456789][0123456789]*\.ext$" ^
/C:"^..* [0123456789][0123456789]* .*\.ext$"
') do (
set "FILE=%%F"
call :SPLIT FIRST NUM REST "%%~nF"
if defined NUM (
setlocal EnableDelayedExpansion
ECHO rename "!_SOURCE!\!FILE!" "!FIRST! !NUM!%%~xF"
endlocal
)
)
endlocal
exit /B
:SPLIT rtn_first rtn_num rtn_rest val_string
setlocal DisableDelayedExpansion
set "RMD=" & set "NUM=" & set "STR=%~4"
:LOOP
for /F "tokens=1,2,* delims= " %%I in ("%STR%") do (
if not "%%J"=="" (
(for /F "delims=0123456789" %%L in ("%%J") do rem/) && (
if not "%%K"=="" (
set "STR=%%J %%K"
goto :LOOP
)
) || (
set "NUM=%%J"
if not "%%K"=="" (
set "RMD=%%K"
)
)
)
)
set "STR=%~4"
if not defined NUM goto :QUIT
set "STR=%STR% "
call set "STR=%%STR: %NUM% =|%%"
for /F "delims=|" %%L in ("%STR:^^=^%") do set "STR=%%L"
:QUIT
(
endlocal
set "%~1=%STR%"
set "%~2=%NUM%"
set "%~3=%RMD%"
)
exit /B
After having tested the script, remove the upper-case ECHO command to actually rename any files.

Related

How to search and replace strings that contain "=" characters in text file with Windows CMD (not powershell)

I wrote this code based on some other examples but just can't get it to work? (it's a .bat file)? The code writes the new file with all the old lines just won't edit the three lines right with the "=" character. Can someone point me in the right direction please.
This is what the INTOUCH.INI file looks like to start:
[InTouch]
AppMode=2
AppName0=test
AppName1=
AppName2=
AppName3=
AppDesc0=New InTouch application
AppDesc1=
AppDesc2=
AppDesc3=
SAOConverted=1
WinFullScreen=1
WinLeft=-4
WinTop=-4
WinWidth=1032
WinHeight=748
UseNewSendKeys=1
DebugScripts=0
UseBigBitmap=1
WindowViewerStartupIconic=0
CloseOnTransfer=0
And this is what is written:
[InTouch]
AppMode=2
AppName0=test
AppName1=
AppName2=
AppName3=
AppDesc0=New InTouch application
AppDesc1=
AppDesc2=
AppDesc3=
SAOConverted=1
1=WinFullScreen=0=1
WinLeft=-4
WinTop=-4
1032=WinWidth=1000=1032
748=WinHeight=700=748
UseNewSendKeys=1
DebugScripts=0
UseBigBitmap=1
WindowViewerStartupIconic=0
CloseOnTransfer=0
This is my .bat file code:
Set "OldString1=WinFullScreen=1"
Set "NewString1=WinFullScreen=0"
Set "OldString2=WinWidth=1032"
Set "NewString2=WinWidth=1000"
Set "OldString3=WinHeight=748"
Set "NewString3=WinHeight=700"
#ECHO OFF &SETLOCAL
cd /d F:\
for %%x in (INTOUCH.INI) do call:process "%%~x"
goto:eof
:process
set "outFile=%~n1_edited%~x1"
(for /f "skip=2 delims=:" %%a in ('find /n /v "" "INTOUCH.INI"') do (
set "ln=%%a"
Setlocal enableDelayedExpansion
set "ln=!ln:*]=!"
if defined ln (
set "ln=!ln:%OldString1%=%NewString1%!"
set "ln=!ln:%OldString2%=%NewString2%!"
set "ln=!ln:%OldString3%=%NewString3%!"
)
echo(!ln!
endlocal
))>"%outFile%"
Exit /b
If you use the file's format to your advantage you can set the values of the new variables at the top of the script and then as you are reading the variable names from the settings file you can see if those variables are defined. If they are defined then output the new value, otherwise output the original value.
The trick to this is the double variable expansion you get when you use the CALL and ECHO commands together. First the for variable is expanded the name of the variable and then in the second phase of expansion the value of the variable is then expanded. That is the reason for the extra sets of percent symbols.
#echo off
Set "WinFullScreen=0"
Set "WinWidth=1000"
Set "WinHeight=700"
REM cd /d F:\
for %%F in (INTOUCH.INI) do set "outFile=%%~nF_edited%%~xF"
REM Read first line of file
set /p line1=<INTOUCH.INI
(echo %line1%
for /f "usebackq skip=1 tokens=1,2 delims==" %%G in ("INTOUCH.INI") do (
if defined %%G (
CALL echo %%G=%%%%G%%
) else (
echo %%G=%%H
)
))>"%outFile%"
Exit /b
#echo off
Set "AppMode=x"
Set "WinFullScreen=0"
Set "WinWidth=1000"
Set "WinHeight=700"
for /f "skip=1 usebackq tokens=1,2 delims==" %%G in ("a.INI") do call :proc "%%G" %%H
exit /b
:proc
set val=%2
for /F "tokens=* eol= " %%S in ("%~1") do set trimmed=%%S
call :getoverrideval %trimmed%
if "%override%" == "" (
echo %~1=%2%
) else (
echo %~1=%override%
)
goto :EOF
:getoverrideval
call set override=%%%1%%
Output:
C:\Users\w16coreeval>cmd /c a.bat
AppMode=x
AppName0=test
AppName1=
AppName2=
AppName3=
AppDesc0=New
AppDesc1=
AppDesc2=
AppDesc3=
SAOConverted=1
WinFullScreen=0
WinLeft=-4
WinTop=-4
WinWidth=1000
WinHeight=700
UseNewSendKeys=1
DebugScripts=0
UseBigBitmap=1
WindowViewerStartupIconic=0
CloseOnTransfer=0

Rename first part of filename using batch

I have some problem writing a code for a batchfile that will replace the first part of a file name.
let say we have the files:
abcd123.txt
abcd345.txt
the numeric part(and the extensions) is the part I want to keep and change it to blabla123.txt and blabla345.txt
the numeric part is not always the same.
I tried to write:
set FILE =%1
set LastPart = %FILE:~-7%
set NewName = c:\MyFolder\blabla%LastPart%
ren %FILE% %NewName%
but it didn't worked because there's space between c:\MyFolder\blabla to 123.txt
Perhaps:
SET "OldName=%~n1"
SET "Ext=%~x1"
SET "LastPart=%OldName:~-3%"
SET "FirstPart=blabla
SET "NewFold=C:\MyFolder"
REN "%~1" "%NewFold%\%FirstPart%%LastPart%%Ext%"
Please see if below script helps you. It iterates through all files in a given directory and renames them according to your requirement.
#echo OFF
setlocal ENABLEDELAYEDEXPANSION
REM Get input directory from user
set /p INPUT_DIR=Please enter full path to directory with files, use double quotes if any space:
cd /d %INPUT_DIR%
for /f %%f in ('dir /b %INPUT_DIR%') do (
set newname=hello!fullname:~-7!
ren %%f !newname!
)
Output
E:>dir /b "E:\Temporary\SO\batch\Input - Space"
adadadadad123.txt
E:>Temporary\SO\batch\test_ren.bat
Please enter full path to directory with files, use double quotes if any
space:"E:\Temporary\SO\batch\Input - Space"
E:>dir /b "E:\Temporary\SO\batch\Input - Space"
hello123.txt
Although the question is not quite clear to me, I decided to provide an answer, because the task of extracting a numeric part from the end of a string appears not to be that trivial, particularly in case both the preceding string and the numeric portions may have different lengths.
So here is a script that accepts file paths/names/patterns provided as command line arguments, splits off ther numeric part, prepends an optional prefix to it and renames the file accordingly (actually it just echoes the ren command line for testing; remove the upper-case ECHO to actually rename):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "PREFIX="
for %%F in (%*) do (
for /F "tokens=1-2 delims=0123456789 eol=0" %%K in ("_%%~nF") do (
if "%%L"=="" (
set "FLOC=%%~F"
set "FILE=%%~nF"
set "FEXT=%%~xF"
set "FNEW="
setlocal EnableDelayedExpansion
set "FILE=_!FILE!"
for /L %%E in (0,1,9) do (
set "NAME=!FILE:*%%E=%%E!"
if not "!NAME!"=="!FILE!" (
if 1!NAME! GTR 1!FNEW! (
set "FNEW=!NAME!"
)
)
)
ECHO ren "!FLOC!" "!PREFIX!!FNEW!!FEXT!"
endlocal
)
)
)
endlocal
exit /B
The script skips all files that have less or more than exactly one numeric part in their names, and also those where the numeric part is followed by something other than the file name extension. For example, abcd1234.txt is processed, whereas abcd.txt, 1234.txt, ab1234cd.txt, 1234abcd.txt and ab12cd34.txt are skipped. Note that the numeric part is limited to nine decimal figures.
If the limit of nine digits is disturbing, the following script can be used. It is very similar to the aforementioned one, but a numeric comparison has been replaced by a string comparison with the numbers padded by leading zeroes to have equal lengths. Therefore the string comparison provides the same result as a pure numeric comparison:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "PREFIX="
set /A "DIGS=256"
setlocal EnableDelayedExpansion
for /L %%E in (1,1,%DIGS%) do set "PADZ=!PADZ!0"
endlocal & set "PADZ=%PADZ%"
for %%F in (%*) do (
for /F "tokens=1-2 delims=0123456789 eol=0" %%K in ("_%%~nF") do (
if "%%L"=="" (
set "FLOC=%%~F"
set "FILE=%%~nF"
set "FEXT=%%~xF"
set "FNEW="
setlocal EnableDelayedExpansion
set "FILE=_!FILE!"
for /L %%E in (0,1,9) do (
set "NAME=!FILE:*%%E=%%E!"
if not "!NAME!"=="!FILE!" (
set "CMPN=%PADZ%!NAME!"
set "CMPF=%PADZ%!FNEW!"
if "!CMPN:~-%DIGS%!" GTR "!CMPF:~-%DIGS%!" (
set "FNEW=!NAME!"
)
)
)
ECHO ren "!FLOC!" "!PREFIX!!FNEW!!FEXT!"
endlocal
)
)
)
endlocal
exit /B
This is a robust and more flexible approach, which allows to specify what numeric part to extract by its (zero-based) index, in the variable INDEX (a negative value counts from the back, so -1 points to the last one, if you prefer that):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "PREFIX=blah" & rem // (optional prefix to be used for the new file names)
set /A "INDEX=0" & rem // (`0` means first numeric part, `-1` means last one)
rem // Loop through command line arguments:
for %%F in (%*) do (
set /A "CNT=-1" & set "KIND="
for /F "delims== eol=" %%E in ('2^> nul set "$PART["') do set "%%E="
rem // Store information about currently iterated file:
set "FLOC=%%~F"
set "FILE=%%~nF"
set "FEXT=%%~xF"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Assemble a list of file name portions of numeric and non-numeric parts:
set "LIST= "!FILE!" "
for /L %%J in (0,1,9) do set "LIST=!LIST:%%J=" %%J "!"
set "LIST=!LIST: "" =!"
rem // Determine file name portions, together with their count and kinds:
for %%I in (!LIST!) do (
endlocal & set /A "CNT+=1"
set "ITEM=%%~I" & set "TEST=%%I"
setlocal EnableDelayedExpansion
if "!TEST!"=="!ITEM!" (set "KND=0") else (set "KND=-")
for /F %%K in ("KIND=!KIND!!KND!") do (
for /F "delims=" %%E in ("$PART[!CNT!]=!ITEM!") do (
endlocal & set "%%K" & set "%%E"
)
)
setlocal EnableDelayedExpansion
)
rem // Retrieve the desired numeric file name portion:
if %INDEX% lss 0 (set /A "INDEX=-(1+INDEX)")
if %INDEX% lss 0 (set "RANGE=!CNT!,-1,0") else (set "RANGE=0,1,!CNT!")
set /A "IDX=-1" & set "FNEW=" & for /L %%J in (!RANGE!) do (
if "!KIND:~%%J,1!"=="0" set /A "IDX+=1" & (
if !IDX! equ !INDEX! for %%I in (!IDX!) do set "FNEW=!$PART[%%J]!"
)
)
rem // Actually rename file:
if defined FNEW (
ECHO ren "!FLOC!" "!PREFIX!!FNEW!!FEXT!"
)
endlocal
)
endlocal
exit /B

Intuitive file sorting in batch scripts?

So in Windows Explorer, I have these files sorted like this:
I have this script to remove the brackets and one zero, and in case the trailing number is greater than or equal to 10, to remove two zeroes:
cd C:\folder
setlocal enabledelayedexpansion
SET /A COUNT=0
for %%a in (*.jpg) do (
SET /A COUNT+=1
ECHO !COUNT!
set f=%%a
IF !COUNT! GTR 9 (
set f=!f:^00 (=!
) ELSE (
set f=!f:^0 (=!
)
set f=!f:^)=!
ren "%%a" "!f!"
)
pause
However, once I run the code, I get this result:
So the batch file isn't going through the files "intuitively" like Windows Explorer shows them. Is there any way to change this? Or is there a better way to rename these files altogether?
This uses a different approach:
#echo off
cd C:\folder
setlocal enabledelayedexpansion
SET /A COUNT=0, REMOVE=2
for /F "delims=(" %%a in ('dir /B *.jpg') do (
SET /A COUNT+=1
ECHO !COUNT!
set "f=%%a"
IF !COUNT! EQU 10 SET "REMOVE=3"
for /F %%r in ("!REMOVE!") do set "f=!f:~0,-%%r!"
ren "%%a" "!f!!COUNT!.jpg"
)
pause
Here is a method that does not depend on the sort order used by the file system, preserving the numbers as occurring in the original file names.
For each file name (for instance, test_this_01 SELR_Opening_00000 (1).jpg), the portion after the last under-score _ is retrieved (00000 (1)). Then the parentheses and the space are removed and then the length is trimmed to five characters (00001). This string replaces the original one in the file name finally (test_this_01 SELR_Opening_00001.jpg); the file name must not contain the replaced portion (00000 (1)) multiple times (hence file names like this should not occur: test_this_00000 (1) SELR_Opening_00000 (1).jpg):
#echo off
setlocal DisableDelayedExpansion
rem // Define constants here:
set "LOCATION=."
set "PATTERN=*_* (*).jpg"
set /A "DIGITS=5"
pushd "%LOCATION%" || exit /B 1
for /F "usebackq eol=| delims=" %%F in (`
dir /B /A:-D /O:D /T:C "%PATTERN%"
`) do (
set "FILE=%%F"
setlocal EnableDelayedExpansion
set "LAST="
for %%I in ("!FILE:_=","!") do (
set "LAST=%%~nI" & set "FEXT=%%~xI"
set "FNEW=!FILE:%%~I=!"
)
set "LAST=!LAST:(=!" & set "LAST=!LAST:)=!"
set "LAST=!LAST: =!" & set "LAST=!LAST:~-5!"
ECHO ren "!FILE!" "!FNEW!!LAST!!FEXT!"
endlocal
)
popd
endlocal
exit /B
Adapt the directory location and the file search pattern in the top section of the script as you like.
After having tested, remove the upper-case ECHO command in order to actually rename files.

Extract substring from filename and count

Example below - 5 files will be located in the same folder.
Sales-fid1000-f100.dat
Revenue-fid1000-f100.dat
Sales-fid2000-f200.dat
Revenue-fid2000-f200.dat
Income-fid2000-f200.dat
I need to read the filename and get the number after "fid", in this case 1000 and 2000 and count the number of files associated with each "fid".
So for fid1000, there are 2 files and for fid2000, there are 3 files.
I need to write the output into a .txt file as below with first field being the fid number and second field being the count.
1000|2
2000|3
How can I generate output text file with fid and count using a Windows batch file?
#echo off
setlocal EnableDelayedExpansion
rem Process all file names
for /F "tokens=2 delims=-" %%a in ('dir /B /A-D *.dat') do (
rem Get FID from second dash-delimited token; format: "xxx-fid####-xxx.dat"
set "fid=%%a"
rem Accumulate it to the corresponding element of "count" array
set /A "count[!fid:~3!]+=1"
)
rem Create the output
(for /F "tokens=2,3 delims=[]=" %%a in ('set count[') do echo %%a^|%%b) > output.txt
For further details on array management in Batch files, see: Arrays, linked lists and other data structures in cmd.exe (batch) script
#ECHO OFF
SETLOCAL
:: remove variables starting $
FOR /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "outfile=%destdir%\outfile.txt"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*-fid*" '
) DO (
SET "filename=%%a"
CALL :process
)
(
FOR /F "tokens=1,2delims=$=" %%a In ('set $ 2^>Nul') DO ECHO(%%a^|%%b
)>"%outfile%"
GOTO :EOF
:process
SET "filename=%filename:*-fid=%"
FOR /f "delims=-" %%q IN ("%filename%") DO SET /a $%%q+=1
GOTO :eof
You would need to change the settings of sourcedir and destdir to suit your circumstances.
Produces the file defined as %outfile%
After clearing all the $ variables (for safety's sake), perform a directory listing without directorynames and in basic form of files in the source directory matching *-fid*.
For each name found, assign the name to filename and execute the :process routine, which first removes the characters up to and including -fid from filename then uses the delims=- option to assign the part originally between -fid and the following - to %%q.
setthe variable $%%q up by 1 (if $?? is undefined, assign 1)
Finally, when all the names have been processed, list the variables named $... using set which produces a report of the style
$1000=2
$2000=3
Using $ and = as delimiters puts token 1 (eg 2000) into %%a and token 2 (eg 3) into %%b. Write these to the output using echo, remembering to escape the pipe (|) with a caret (^) to suppress the interpretation as a redirector.
The parentheses around the for...$... ensures the output is directed to the destination file specified.
Extract the numbers into a temporary file, then count the occurrences of each number in that file.
#echo off
setlocal EnableDelayedExpansion
>temp.txt type nul
set "unique_num="
for /f "tokens=2 delims=-" %%a in ('dir /b *.dat') do (
set "fid=%%a"
set "num=!fid:~3!"
>>temp.txt echo !num!
echo " !unique_num! " | find " !num! " >nul
if !errorlevel! neq 0 set "unique_num=!unique_num! !num!"
)
for %%n in (%unique_num%) do (
for /f "delims=: tokens=2" %%c in ('find /c "%%n" temp.txt') do (
set "count=%%c"
echo %%n^|!count: =!
)
)
del /f /q temp.txt
Pipe the result into sort if you need the output sorted.

Batchfile: read last lines from logfiles and copy them to a new file

This is my first posting so if the format is not as it supposed to be please excuse me for this. (Suggestions for
improvement are welcome.)
I am trying to create a batchfile that will read last lines from logfiles and copy them to a new file.
Until now I have found here a way to read the last line.
Code would be something like:
for /f %%i in ('find /v /c "" ^< someFile.txt') do set /a lines=%%i
set /a startLine=%lines% - 1
more /e +%startLine% someFile.txt > lastLines.txt
The above code works for one file at a time. What I need is to read the last line from all files in a known list and add this line to a new .csv file.
I have been using the following code for getting the 4th entry in the logfiles but it returns every line of every logfile:
for /f %%x in (%list%) do for /f "delims=.txt, tokens=4" %%i in (%%x.txt) do echo %%x, %%i >> output.csv
What I would need is a sort of combination of both but I don't know how to combine them and make the complete last line be copied to the .csv file.
===
#Magoo:
Thanx for your reaction.
In every logfile can be 1 to >100 lines with comma separated information. Something like:
"LOGON,6-1-2015,12:43:39,USERNAME,HOSTNAME,,,,192.168.209.242,00:21:5A:2E:64:5E"
The last code with the 4th entry was used to get a list of all accounts that had logged in to the computers. This code gave me a very large list of all logon/logoff events on all computerlogs I checked in %list%.
In %list$ I had all the names of logfiles I wanted to be checked. This returned all lines.
For a new batchfile I need only the last logon/logoff entry and I want the whole last line.
So I have a .txt file with the hostnames of all computers I need to examine.
This .txt file will be read line by line via the variable %list%.
From every logfile I need only the last line copied to an output file.
===
I just tried the solution offered by JosefZ. Unfortunately this does not work for me yet. No lastlines are copied to the resultfile. In the code I removed the extra entry for possible lastlines for there are no empty lines in the logs, I also added an entry for the hostname I want to be available in the result. JosefZ had the filename there:
#ECHO OFF >NUL
#SETLOCAL enableextensions disabledelayedexpansion
type nul>output.csv
set "list=_listing.txt"
for /F "tokens=*" %%x in ('type "%list%"') do (
set "host=%%~x"
for /F "tokens=*" %%G in ('type "%%~x"') do set "lastline=%%G"
call :lline
)
:endlocal
#ENDLOCAL
goto :eof
:lline
set "filename=.\logs\%filename:&=^&%.txt"
echo %host%,%lastline%>>output.csv
goto :eof
The resultfile shows only the hostnames. I'll puzzle some more with this but all tips are welcome!
===
Got it!!!
#ECHO OFF >NUL
#SETLOCAL enableextensions disabledelayedexpansion
type nul>output.csv
set "list=_listing.txt"
for /F "tokens=*" %%x in ('type "%list%"') do (
set filename= :: *empty previous filename*
set lastline= :: *empty previous lastline*
set "host=%%~x"
set "filename=.\logs\%host%.txt" :: *creating the filename from path+hostname+extention*
for /F "tokens=*" %%G in ('type "%filename%"') do set "lastline=%%G"
call :lline
)
:endlocal
#ENDLOCAL
goto :eof
:lline
echo %host%,%lastline%>>output.csv
goto :eof
Your approach with line numbering could fail if a file has more trailing empty lines. Fortunately for /F loop ignores (does not iterate) empty lines; let's put to use this feature: in the script used next practices:
disabledelayedexpansion to allow ! in file names
set "list=_listing.txt" where the _listing.txt contains list of file names (full path and extension .txt including), one file name on one line: got by dir /b /s *.txt>_listing.txt
type nul>files\output.csv to empty the output file (optional)
set "lastline=!!!file empty!!!" to initialize variable %lastline%; could be set "lastline=" as well
call :lline to process variables %filename% and %lastline%
set "filename=%filename:&=^&%" to allow & in file names
The script is as follows:
#ECHO OFF >NUL
#SETLOCAL enableextensions disabledelayedexpansion
type nul>files\output.csv
set "list=_listing.txt"
for /F "tokens=*" %%x in ('type "%list%"') do (
set "filename=%%~x"
set "lastline=!!!file empty!!!"
rem the whole line
for /F "tokens=*" %%G in ('type "%%~x"') do set "lastline=%%G"
rem the fourth token only
rem for /F "tokens=4" %%G in ('type "%%~x"') do set "lastline=%%G"
call :lline
)
:endlocal
#ENDLOCAL
goto :eof
:lline
set "filename=%filename:&=^&%"
echo %filename% %lastline%
rem >>files\output.csv
goto :eof
Sample _listing.txt file:
d:\bat\files\1exclam!ation.txt
d:\bat\files\2exc!lam!ation.txt
d:\bat\files\11per%cent.txt
d:\bat\files\12per%cent%.txt
d:\bat\files\17per%Gcent.txt
d:\bat\files\18per%%Gcent.txt
d:\bat\files\21ampers&nd.txt
d:\bat\files\22ampers&&nd.txt
Output:
d:\bat>lastlines
d:\bat\files\1exclam!ation.txt 0 15.01.2015 1:52:28.48 -15072 20465
d:\bat\files\2exc!lam!ation.txt 6 15.01.2015 1:52:28.50 3250 16741
d:\bat\files\11per%cent.txt -8 15.01.2015 1:52:28.50 -3692 27910
d:\bat\files\12per%cent%.txt !!!file empty!!!
d:\bat\files\17per%Gcent.txt 0 15.01.2015 1:52:28.56 14508 12374
d:\bat\files\18per%%Gcent.txt 1 15.01.2015 1:52:28.56 30540 26959
d:\bat\files\21ampers&nd.txt 15.01.2015 1:22:50.18
d:\bat\files\22ampers&&nd.txt 15.01.2015 1:22:50.18
Honestly, all that ballast is for (possibly) trailing empty lines in files and for (possibly) ! and & in file names only; all could be done with
for /f %%x in (%list%) do for /f "skip=%startLine% tokens=4" %%i in (%%x) do echo %%x, %%i >> output.csv
You should use a simple FOR to iterate a list of values, not FOR /F.
Something like the following should work:
#echo off
setlocal enableDelayedExpansion
>>output.csv (
for %%F in (
"file1.log"
"file2.log"
"file3.log"
etc.
) do (
for /f %%A in ('find /v /c "" <%%F') do set /a skip=%%A-1
more +!skip! %%F
)
)
The quotes around the file names are there in case you get a name with spaces.
You could use your LIST variable if it looks something like
set LIST="file1.log" "file2.log" "file3.log" etc.
#echo off
setlocal enableDelayedExpansion
set LIST="file1.log" "file2.log" "file3.log" etc.
>>output.csv (
for %%F in (%LIST%) do (
for /f %%A in ('find /v /c "" <%%F') do set /a skip=%%A-1
more +!skip! %%F
)
)
If any of your file names contain the ! character, then you must toggle delayed expansion ON and OFF within your loop. Otherwise the delayed expansion will corrupt the names when %%F is expanded.
#echo off
setlocal disableDelayedExpansion
set LIST="file1.log" "file2.log" "file3.log" etc.
>>output.csv (
for %%F in (%LIST%) do (
for /f %%A in ('find /v /c "" <%%F') do set /a skip=%%A-1
setlocal enableDelayedExpansion
more +!skip! %%F
endlocal
)
)

Resources