Numbering text files in a folder - batch-file

I have some text files placed in the same folder as a batch file. I need the batch file to echo the filenames without its extention line by line with a numbering. It has to be in alphabetical order. Also, it needs to store the file name into a variable fruitx where x is the number of its numbering. For example...
If I have three files in the folder:
Banana.txt
Cherry.txt
Apple.txt
I need the batch file to echo:
1) Apple
2) Banana
3) Cherry
Then, I need the variable %fruit1% be "Apple", %fruit2% be "Banana" and %fruit3% be "Cherry".
set x=0
for /F "tokens=* delims=" %%I in ('dir /b *.txt') do (
set /a x=x+1&echo %x%^) %%~nI&set fruit%x%=%%~nI)
echo %x%
Here is my code. It doesn't work and I can't figure out why. It echos 0 for each of the numberings instead.
Sorry if this sounds confusing. Thanks in advance!

When the cmd parser reads a line or a block of lines (the code inside the parenthesis), all variable reads are replaced with the value inside the variable before starting to execute the code. If the execution of the code in the block changes the value of the variable, this value can not be seen from inside the same block, as the read operation on the variable does not exist, as it was replaced with the value in the variable.
This same behaviour is seen in lines where several commands are concatenated with &. The line is fully parsed and then executed. If the first commands change the value of a variable, the later commands can not use this changed value because the read operation replace.
To solve it, you need to enable delayed expansion, and, where needed, change the syntax from %var% to !var!, indicating to the parser that the read operation needs to be delayed until the execution of the command.
In your case, the problematic variable is x, that changes its value inside the loop and needs this value inside the same loop
#echo off
setlocal enableextensions enabledelayedexpansion
set "x=0"
for /f "delims=" %%a in ('dir /a-d /b /on *.txt 2^>nul') do (
set /a "x+=1"
echo !x!^) %%~na
set "fruit!x!=%%~na"
)
echo(---------------------------
set fruit
Also, an alternative approach without delayed expansion can be to filter the generated list of files with findstr to generate the number for each element
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "tokens=1,2 delims=:" %%a in ('dir /a-d /b /on *.txt 2^>nul ^| findstr /n "^"') do (
echo %%a^) %%~nb
set "fruit%%a=%%~nb"
)
echo(---------------------------
set fruit

#echo off
setlocal enableDelayedExpansion
set counter=0
for /f %%a in ('dir /b /a:-d /o:n *.txt') do (
set /a counter=counter+1
echo !counter! ^) %%~na
set "fruit!counter!=%%~na"
)
echo listing fruits
set fruit

Related

Batch file to rename text files within directory with text from within text file at specific line except if line blank

#Echo off&SetLocal EnableExtensions EnableDelayedExpansion
cd "C:\Documents and Settings\John\Desktop\New\Interest\f2"
Pushd "C:\Documents and Settings\John\Desktop\New\Interest\f2"
Set Line#=26
Set /A LOfs=24 -1, Len=34 - LOfs
For %%A in (*.txt) do For /F "tokens=1* delims=:" %%B in (
'Findstr /N ".*" "%%A" ^|Findstr "^%Line#%:"'
) do if %errorlevel% == 0 Set "Line=%%C"&Ren "%%~fA" "!Line:~%LOfs%,%Len%! - %%A!""
Popd
In the above I am trying to change the filename of files in a directory with text in it at a certain position.
If line 26 is blank do nothing and do not change filename.
I have gone wrong somewhere and am going round in circles.
Can anyone help?
Thank you.
You don't state how your script fails, but I can see some potential problems. I also see possible simplifications.
You certainly don't need both CD and PUSHD
I got rid of the numeric variables and included the number literals in the actual code. You can revert back to variables if you want.
You don't need the outer FOR loop. FINDSTR can search multiple files when using wildcards in the file name, and then it will include the filename, followed by : in the output. So if you add the /N option, output will have the form filename:line#:text. You can then adjust the 2nd FINDSTR to return only the correct line numbers.
It is not enough to ignore blank lines. Your rename only works if there is at least one valid file name character after the 23rd character. Filenames cannot include :, *, ?, /, \, <, >, or |. (I may have missed some). I adjusted the FOR /F delims and the FINDSTR search to compensate.
FOR variable expansion like %%A will corrupt values if they contain ! and delayed expansion is enabled. ! is a valid character in file names. So the delayed expansion must be toggled on and off within the loop.
I believe the following will do what you want. The code below will simply echo the rename commands. Remove the ECHO before the ren once it gives the correct results.
#echo off
setlocal disableDelayedExpansion
pushd "C:\Documents and Settings\John\Desktop\New\Interest\f2"
for /f "tokens=1,3 delims=:*?\/<>|" %%A in (
'findstr /n "^" "*.txt" ^| findstr "^[^:]*:26:.......................[^:*?\\/<>|]"'
) do (
set "old=%%A"
set "line=%%B"
setlocal enableDelayedExpansion
ECHO ren "!old!" "!line:~23,11! - !old!"
endlocal
)
popd
An slightly different method to Daves:
#Echo Off
Set "SrcDir=%UserProfile%\Desktop\New\Interest\f2"
Set "Mask=*.txt"
Set "Line#=26"
Set "LOfs=23"
Set "Len=11"
If /I Not "%CD%"=="%SrcDir%" Pushd "%SrcDir%"2>Nul&&(Set _=T)||Exit/B
For /F "Tokens=1-2* Delims=:" %%A In ('FindStr/N "^" "%Mask%" 2^>Nul'
) Do If "%%B"=="%Line#%" If Not "%%~C"=="" (Set "Line=%%C"
SetLocal EnableDelayedExpansion
If Not "!Line:~%LOfs%,%Len%!"=="" (
If Not Exist "!Line:~%LOfs%,%Len%! - %%A" (
Ren "%%A" "!Line:~%LOfs%,%Len%! - %%A"))
EndLocal)
If "_"=="T" PopD
This method don't require findstr.exe nor toggle setlocal/endlocal, so it should run faster. Also, it avoids to re-process any already renamed file changing the plain for %%A by a for /F combined with dir command.
#Echo off
SetLocal EnableDelayedExpansion
cd "C:\Documents and Settings\John\Desktop\New\Interest\f2"
Set /A Line#=26, LOfs=24 -1, Len=34 - LOfs
For /F "delims=" %%A in ('dir /A-D /B *.txt') do (
rem Read the desired line from this file
(for /L %%i in (1,1,%Line#%) do set "Line=" & set /P "Line=") < "%%A"
if defined Line ECHO Ren "%%~fA" "!Line:~%LOfs%,%Len%! - %%A"
)
Note also that when this Batch file ends the current directory is automatically recovered to the current one when setlocal command was executed, so pushd/popd commands are not needed either.

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
)
)

Rename multiple files in a directory using batch script

I have about 1000 images and they have name like "IMG-12223". I want to rename them to 1 2 3 4 ... 1000. How can I do that. I have written a batch script which list the files but I don't know how to rename each file. e.g. rename first image with name "IMG-12223" to 1 , second image with name "IMG-23441" to 2 and so on ...
for /r %%i in (*) do (
echo %c%
)
Here's the script. Just put the script in your folder and run it.
#echo off & setlocal EnableDelayedExpansion
set a=1
for /f "delims=" %%i in ('dir /b *') do (
if not "%%~nxi"=="%~nx0" (
ren "%%i" "!a!"
set /a a+=1
)
)
If you want to keep the extensions, i.e. rename "IMG-12223.jpg", "IMG-12224.jpg", etc to "1.jpg", "2.jpg", etc, you may use the following script.
#echo off & setlocal EnableDelayedExpansion
set a=1
for /f "delims=" %%i in ('dir /b *.jpg') do (
ren "%%i" "!a!.jpg"
set /a a+=1
)
[Update] Here're explanations for the lines mentioned in Jack's comment.
setlocal EnableDelayedExpansion
In general, we want the variable a to be delayed expansion when it's executed but not the line is read. Without it, the variable a cannot get its increased value but always 1.
For the detail of EnableDelayedExpansion, please refer to the answer https://stackoverflow.com/a/18464353/2749114.
for /f "delims=" %%i in ('dir /b *.jpg')
Here dir with /b option, lists only file names of all jpg files.
The for loop traverses and renames all jpg files.
For the delims option, since the default delimiter character is a space, without the option delims=, it fails with the image files with spaces in the file names. I.E. for an image file named "img with spaces.jpg", without the option, the value of %%i is "img" but not the whole name "img with spaces.jpg", which is incorrect.
For for loop, please refer to the page http://ss64.com/nt/for_f.html.
if not "%%~ni"=="%~n0"
I have change it to if not "%%~nxi"=="%~nx0" to be more accurate. And the codes attached have been updated.
It's actually used to avoid to rename the bat file itself. If we limit the renaming only upon "jpg" files, then the line is not needed.
%%~nxi is the file name with extension for each file traversed. And %~nx0 is the running bat file with extension. For details, please refer to the page DOS BAT file equivalent to Unix basename command?.
There is no need for a batch script. A simple one liner from the command line can do the job :-)
I use DIR /B to generate the list of files, piped to FINDSTR to number the files, all enclosed withn FOR /F to parse the result and perform the rename.
for /f "delims=: tokens=1*" %A in ('dir /b *.jpg^|findstr /n "^"') do #ren "%B" "%A%~xB"
Double the percents if you want to put the command in a batch script.
Try this, you have pair of namevalues in a text file then loop values and do the magic. Namevalues are separated by empty spaces. This allows you to map old->new filenames accordingly. Or you keep idx+1 counter and use it for new filenames.
keyvalue.bat
#echo off
set idx=0
for /F "tokens=1,2" %%A in (keyvalue.txt) do call :PROCESS "%%A" "%%B"
GOTO :END
:PROCESS
set var1=%~1
set var2=%~2
set /A idx=%idx%+1
echo %var1% goes to %var2% (%idx%)
GOTO :EOF
:END
pause
keyvalue.txt
file888.dat newfile1.dat
file333.dat newfile2.dat
file9.dat newfile3.dat
file01.dat newfile4.dat

Modifying variable values inside a For loop in Windows shell script

I need to perform operations on first 4 files in a folder. So, the counter variable has to increment during each operation of the For loop. The following piece of code:
#ECHO OFF
SETLOCAL
SET TESTDIR1=D:\Local Data\Shell Script\test folder 1
SET _transfer=XXXX
FOR /f "delims=" %%i IN ('dir "%TESTDIR1%\*" /b/a-d/o-d') DO (
SET _transfer=%_transfer:~1%
ECHO %_transfer%
)
prints "XXXX" for every iteration of the loop. Why might that be?
Try
FOR /f "TOKENS=1*delims=[]" %%i IN ('dir "%TESTDIR1%\*" /b/a-d/o-d^|find /n /v ""') DO (
if %%1 LEQ 4 echo %%j
)
Your approach doesn't work because batch replaces any %var% within a block (a parenthesised sequence of commands) with the value of var before the block is executed. See any number of SO questions related to delayed expansion for more info.

How to get the list of filenames in a directory and store that in a variable using cmd commands

I need to get all the filenames in a directory and store them in some variable from a command line.
I came across this
`dir /s /b > print.txt`
but this prints the file names to a txt file.
How can I store these names in a variable?
I'm assuming you really mean Windows batch file, not DOS.
Batch environment variables are limited to 8191 characters, so likely will not be able to fit all the file paths into one variable, depending on the number of files and the average file path length.
File names should be quoted in case they contain spaces.
Assuming they fit into one variable, you can use:
#echo off
setlocal disableDelayedExpansion
set "files="
for /r %%F in (*) do call set files=%%files%% "%%F"
The CALL statement is fairly slow. It is faster to use delayed expansion, but expansion of %%F will corrupt any value containing ! if delayed expansion is enabled. With a bit more work, you can have a fast and safe delayed expansion version.
#echo off
setlocal disableDelayedExpansion
set "files=."
for /r %%F in (*) do (
setlocal enableDelayedExpansion
for /f "delims=" %%A in ("!files!") do (
endlocal
set "files=%%A "%%F"
)
)
(set files=%files:~2%)
If the file names do not fit into one variable, then you should resort to a pseudo array of values, one per file. In the script below, I use FINDSTR to prefix each line of DIR ouptut with a line number prefix. I use the line number as the index to the array.
#echo off
setlocal disableDelayedExpansion
:: Load the file path "array"
for /f "tokens=1* delims=:" %%A in ('dir /s /b^|findstr /n "^"') do (
set "file.%%A=%%B"
set "file.count=%%A"
)
:: Access the values
setlocal enableDelayedExpansion
for /l %%N in (1 1 %file.count%) do echo !file.%%N!
As #Matt said, use a batch file.
setlocal enabledelayedexpansion
set params=
for /f "delims=" %%a in ('dir /s/b') do set params=!params! %%a
setlocal enableDelayedExpansion
set /a counter=0
for /f %%l in ('dir /b /s') do (
set /a counter=counter+1
set line_!counter!=%%l
)
set line_
If you want to store all in one variable check this:
Explain how dos-batch newline variable hack works

Resources