Using batch to count the number of specific files in folder - batch-file

I have a bunch of files in a folder that their names are like a1.txt, a6.txt, a8.txt,..and I need to count them I tried this batch file but it does not recognize * as a way to account for all numbers and does not return the correct answer.
set /a count=0
for /F "tokens=* delims= " %%i in ('dir/s/b/a-d "C:\Users\xyz\desktop\Project\a.*"') do (set /a count=count+1)
Can you see what I am doing wrong? Thanks for your help in advance.

You could do this in a single line
dir /a-d "C:\Users\xyz\desktop\Project\a.*" | find /C "/"
Explanation:
dir is the directory listing command. The /a starts an attribute filter and the -d tells dir to not list directories. So all files in that directory with a. as the start of the filename are piped to the find command.
The find command has a built in /C option to count the lines and lines in this case are files.

What you're doing wrong is the wildcard for all files starting with a.
You're using dir a.* and expecting it to find files like a6.txt
Also, to handle filenames with spaces, I suggest you remove the delimiters.
set /a count=0
for /F "delims=" %%i in ('dir/s/b/a-d "C:\Users\xyz\desktop\Project\a*"') do (set /a count=count+1)
( Also listen to the other answers in terms of making your code more efficient. )

Faced with a similiar problem, I prefer to use a trick I learned from Raymond Chen, wich is to use find as a replacement for wc -l.
So the following scrit sets the batch variable count to the number of files that match patttern. Like your original script, directories are excluded from the count.
#echo off
setlocal
set count=0
set pattern=a?.txt
use dir /b %pattern% ^| find /c /v ""
for /f %%i in ('dir /b /a-d %pattern_you_are_looking_for% ^| find /c /v ""') do #call set count=%%i
echo %count%
endlocal

You have your asterisk in the wrong place. I think you intended a*.txt. But that will match any text file whose name begins with "a". It does not limit the results to text files that start with "a", followed by a number.
You can pipe the results of your DIR command to FINDSTR and use a regular expression to be more specific. The FINDSTR regex support is primitive, but it often gets the job done.
I'm going to assume you want to match names like "a1.txt", "a143.txt", but you don't want to match files like "a1b.txt" or "aba1.txt". If I got that wrong then you need to change the regex expression to match your requirements.
This regex \\a[0-9][0-9]*\.txt$ works as follows:
\\ is an escaped backslash, matching the last backslash before the file name
a matches itself of course
[0-9] matches a single digit (there must be at least 1)
[0-9]* matches 0 or more additional digits
\.txt$ escapes the dot and matches the ".txt" extension. The $ matches the end of the string - it will not match if additional characters follow.
The last thing to do is pipe the result of FINDSTR to FIND to let it count the number of files for you. FIND /C /V "" matches any line and the /C option gives the count of matching lines. It is more efficient than incrementing the counter in your loop.
#echo off
setlocal
set /a count=0
for /F %%N in ('dir/s/b/a-d "C:\Users\xyz\desktop\Project\a*.txt"^|findstr /ric:"\\a[0-9][0-9]*\.txt$"^|find /c /v ""') do set count=%%N
echo count=%count%

here's a little bit of a sneaky way to count:
for /f "tokens=1 usebackq" %a in (`dir a* ^| find ^"File^(s^)^"`) do set count=%a
this can be done from the command line - change to double % for a batch file
there is a lot of escaping (using ^) to stop the following characters getting interpreted as part of the batch file, instead of being passed to the command line
The command being executed is dir a* | find "File(s)" , but |,(,) and " tend to have special meanings

#SetLocal enabledelayedexpansion
#for /F "tokens=1" %%a IN ('Dir "..\*.txt" /-C/S/A:-D') Do #Set number_of_files=!n2! & Set n2=%%a
#echo %number_of_files%

Related

Remove last 4 characters of a file in a folder with a batch file

this should be pretty simple, i just want a bat file that when placed in a folder will rename all files within the folder and delete the final 4 characters, but not the extension
ex.
img (1).jpg
becomes
img.jpg
ive tried
#echo off
setlocal enabledelayedexpansion
if not exist %1 goto :eof
for /f %%A in ('find /V /C "" ^<%1') do set lines=%%A
for /f "tokens=1,* delims=[]" %%A in ('find /V /N "" ^<%1') do (
if %%A LSS %lines% (
echo %%B
) else (
set rec=%%B
echo !rec:~0,-4!
)
)
but it doesnt work. theres no error, it runs, it just doesnt do anything
I modified your code as follows:
1/ switched to a test directory (PUSHD testdirectoryname) and back (POPD)
2/ changed the %1 to a fixed filename
3/ Inserted some debug lines
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following setting for the source directory includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
PUSHD "%sourcedir%"
if not exist q71992787.txt goto :eof
TYPE "%sourcedir%\q71992787.txt"
for /f %%A in ('find /V /C "" ^<q71992787.txt') do set lines=%%A
SET li
find /V /N "" <q71992787.txt
for /f "tokens=1,* delims=[]" %%A in ('find /V /N "" ^<q71992787.txt') do (
if %%A LSS %lines% (
echo %%B
) else (
set rec=%%B
echo !rec:~0,-4!
)
)
POPD
GOTO :EOF
The result, other than the debug lines, was a list of the filenames in the file
q71992787.txt, other than the very last filename, which was shortened by 4 characters.
This differs from your result "it just doesnt do anything". This is why I mention it.
So - to your solution:
I can see no reason for calculating the number of lines in the file. If there are 10 lines for example, then reading the same file again with 'find /v /n` will still find 10 lines.
The if %%A LSS %lines% will echo %%B (the filename) if %%A (the line number) is LESS (LSS) than 10; that is, from 1 to 9 - just show the filename.
The else clause will only be processed if %%A is 10 or greater, so only on the last line.
I'm sure this wasn't what you intended to do.
I'd advise you to use set "var1=value" for setting STRING values - this avoids problems caused by trailing spaces. Quotes are not needed for setting arithmetic values (set /a`)
Where you alter your filename, setting rec to %%B means the entire filename, %%B. You don't show us a sample from the file %1, so I'll presume it's img (1).jpg as your narrative mentions. rec will thus become img (1).jpg and then be shortened to img (1) by removing the last 4 characters of the string.
Since you seem to want to remove the last 4 characters of the name part and leave the extension, then you need to read the documentation for for (for /? from the prompt) which will tell you
set "rec=%%~nB"
will assign just the name part and you could then
set "rec=!rec:~0,-4!%%~xB"
echo !rec:~0,-4!%%~xB
to set or show the manipulated name.
As to why you get no result at all; This could be because the file encoding of %1 is unicode or is *nix format (, not line endings)
And I do hope you're not using a *nix emulator like cygwin where find becomes a file-locator, not a string-analyser.
The file renaming task for all *.jpg and *.jpeg files in the current directory of which file name ends with a space and a single digit number in round brackets can be done with a batch file with following command lines:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "eol=| delims=" %%I in ('dir "* (?).jpg" "* (?).jpeg" /A-D /B /ON 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /C:" ([0123456789])\.jpe*g$"') do (
set "FileName=%%~nI"
setlocal EnableDelayedExpansion
ren "!FileName!%%~xI" "!FileName:~0,-4!%%~xI"
endlocal
)
endlocal
That code works also for image file names containing one or more exclamation marks and one or more round brackets before the last four characters in file name to remove.
Note: The command REN outputs an error message if the current directory contains the two files photo (1).jpg and photo (2).jpg on renaming the second file to photo.jpg because of this is not possible after first file already renamed to photo.jpg. There cannot be two files with same name in same directory.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
dir /?
echo /?
endlocal /?
findstr /?
ren /?
set /?
setlocal /?
Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul and |. The redirection operators > and | must be escaped with caret character ^ on FOR command line to be interpreted as literal characters when Windows command interpreter processes this command line before executing command FOR which executes the embedded command line with using a separate command process started in background.
Doing it in PowerShell would be much easier
ls *.jpg |% { ren $_ -ne ($_.BaseName.Substring(0, $_.BaseName.Length - 4) + $_.Extension) -wi }
-wi or -WhatIf is the option for dry running. After checking that the new names are OK you can remove that option. Full command line without alias:
Get-ChildItem -File *.* | ForEach-Object { Rename-Item -WhatIf -Path $_ `
-NewName ($_.BaseName.Substring(0, $_.BaseName.Length - 4) + $_.Extension) }

Counting the number of .txt files in a directory

I need to count the number of .txt files in a Windows 10 directory.
I am starting with the following code as a batch-file:
#ECHO OFF
for %%f in (*.*) do echo %%f
Does anyone know how I can do it?
The method will most likely depend upon the rest of the script; but for the question only.
Probably the simplest method would be to use where.exe and find.exe together:
Where .:*.txt 2>Nul|Find /C /V ""
Should you wish to save the count as the value content of a variable, you can use a For /F loop incorporating either where.exe or the Dir command. Please note however that in both a standard For loop, (For %%A In (*.txt)), and when using the Dir command, (Dir *.txt), file extensions beginning with .txt are returned as opposed to those ending with .txt, returned by where.exe. In order to account for this, in my Dir version, I have piped the results through a findstr.exe to ensure that only .txt files are counted:
Where method:
#Echo Off
Set "#=0"
For /F %%A In ('"Where .:*.txt 2>Nul|Find /C /V """')Do Set "#=%%A"
Echo(%#%
Pause
Dir method:
#Echo Off
Set "#=0"
For /F %%A In ('"Dir/B/A-D-L *.txt 2>Nul|FindStr/I "txt$"|Find /C /V """'
)Do Set "#=%%A"
Echo(%#%
Pause

Findstr based on column1 only [batch file]

I have been searching through StackOverflow but could not find an answer that hits the mark. I have 2 .txt files to compare and return a 3rd one where differences exist.
However, only the first column of the first 2 files need a comparison.
E:\Compare_flie\file_1.txt
GND ZERO
22XC44 XXYYZZ
33XC55 YYUUTT
E:\Compare_file\file_2.txt
GND ZERO
22XC44 KK77UU
33XC55 88JJ66
66NN77 HHOO99
99CC88 UU77RR
E:\Compare_file\file_3.txt (intended output)
66NN77 HH0099
99CC88 UU77RR
Tried the code below but it is only good at picking out the differences of all the strings in the line
%echo on
findstr /v /i /g:E:\Compare_files\file_1.txt E:\Compare_files\file_2.txt
> E:\Compare_files\file_3.txt
Refined it further but not hitting the mark yet.
%echo on
for /f "tokens=1 delims= " %%I in ("E:\Compare_files\file_1.txt") do
findstr /v /i "%%I"/g:"D:\Compare_files\file_2.txt"
> "D:\Compare_files\file_3.txt"
Appreciate if anyone can assist.
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "filename1=%sourcedir%\q48816766.txt"
SET "filename2=%sourcedir%\q48816766_2.txt"
SET "tempfile=%temp%\q48816766.tmp"
SET "outfile=%destdir%\outfile.txt"
(FOR /f "usebackq" %%a IN ("%filename1%") DO ECHO %%a )>"%tempfile%"
FINDSTR /b /v /g:"%tempfile%" "%filename2%">"%outfile%"
REM DEL "%tempfile%" /F /Q
GOTO :EOF
I've set up names to suit my system, but with the two files containing your data.
Obviously, the usebackq on the for/f is only required if the filename is quoted. The parentheses around the command permit the echoed output to be accumulated into the temporary file. What's important here is the space between the %%a and ). This ensures that the temporary file contains trailing spaces.
Then apply the temporary file to the second data file via /g as in OP's code. The presence of the trailing spaces in the tempfile ensure that the only lines selected for omission are those where the first column exactly matches so for instance had 66NN7 appeared in the first file, first column, then this would not match 66NN77 in the second file.
Here's a method using batch with the type command piping the first file's contents over to the findstr command then passing the arguments accordingly to redirect those results into a temp file.
Using a for /f loop with "usebackq tokens=1 delims= " it will iterate through the temp file and for each line in that file parsing accordingly, it will append the column one lines with an echo command using >> to redirect the results over to file_3.txt with the expected results.
Please note the addition of the if exist "%srcdir%\file_3.txt" del /q /f "%srcdir%\file_3.txt" to delete that file if it exists since the for /f echo commands will append one after the other to it.
#echo on
set srcdir=E:\Compare_files
set tmpfile=%temp%\%~N0.tmp
type "%srcdir%\file_1.txt" | findstr /vig:"%srcdir%\file_2.txt">"%tmpfile%"
if exist "%srcdir%\file_3.txt" del /q /f "%srcdir%\file_3.txt"
for /f "usebackq tokens=1 delims= " %%I in ("%tmpfile%") do (
echo %%~I>>"%srcdir%\file_3.txt"
)
Further Resources
FOR /F
FOR /?
usebackq - specifies that the new semantics are in force,
where a back quoted string is executed as a
command and a single quoted string is a
literal string command and allows the use of
double quotes to quote file names in
file-set.
Redirection
Comparing/finding the difference between two text files using findstr
As I understand your problem, you want the lines from file_2.txt whose first column are not contained in first column of file_1.txt, that is: file_2.txt minus file_1.txt. There is a simpler approach to get such result:
#echo off
setlocal
rem Fill "line" array with lines from file_2.txt
rem use the first column for the array keys
for /F "delims=" %%a in (file_2.txt) do for /F %%b in ("%%a") do set "line[%%b]=%%a"
rem Delete array elements with same key from file_1.txt
for /F %%b in (file_1.txt) do set "line[%%b]="
rem Show remaining elements
(for /F "tokens=1* delims==" %%a in ('set line[') do echo %%b) > file_3.txt

Batch File to Output Subfolders sorted by Date Modified and # of Files

I am trying to write a simple batch file that will list the subfolders that begin with "#" of a particular folder in which the batch file resides, sort those files by date modified, and tell me the number of files within each subfolder. Here is my code, which works perfectly except that it always outputs the number of files as "0" which is incorrect:
FOR /F "tokens=* delims= " %%D IN ('DIR *.* /-P /O:G-DE ^| FIND "#"') DO (
FOR /F %%K IN ('DIR "%%D" 2^>NUL ^| FIND "File(s)" ^|^| ECHO 0') DO (
ECHO %%D %%K>>Test1.txt
)
)
When I do a test batch file just to output the number of files in the subfolders, it works (although it gives the number of files in the folder and total number of files as well, but at least I'm getting the number of files per subfolder):
FOR /F %%K IN ('DIR /S ^| FIND "File(s)"') DO ECHO %%K >>Test2.txt
I just can't seem to get it to work when I combine the two bits of code to simply give me the names of the subfolders + number of files (one line per subfolder). Any help would be greatly appreciated!
Thank you!
The command line dir *.* /-P /O:G-DE | find "#" you are using actually returns files and directories that contain #. To get only directories that begin with #, use the command line dir /-P /A:D /O:-DE "#*". The switch /A:D lets dir return directories only; the pattern #* means items beginning with #, there is no need for find.
Now let us use for /F to walk through all items. For this, add the switch /B to dir to avoid any headers in the output and return a pure directory list:
for /F "delims=" %%D in ('
dir /B /-P /A:D /O:-DE "#*"
') do (
rem print each item:
echo "%%~D"
)
So let us concentrate on the number of files contained in each of the listed directories now. Your method using dir and searching for the line containing the text File(s) by find works fine basically. There is no need for echo 0 as dir returns the summary line also for empty directories. But you need to consider items that contain the string File(s) in their names. So the following code should work:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
> "Test1.txt" (
for /F "delims=" %%D in ('
dir /B /-P /A:D /O:-DE "#*"
') do (
for /F "tokens=1 delims= " %%K in ('
dir /-P "%%~D" ^| find "File(s)"
') do (
set "numItems=%%K"
)
echo "%%~D" !numItems!
)
)
endlocal
The redirection to a text file is done once rather than for every loop iteration. I also removed the 2> nul from the inner dir command as the enumerated directory will exist for sure (it is retrieved by the outer dir), so there will be no error messages.
The inner loop does not produce an output but assigns the number of files to a variable numItems. This loop normally iterates once, but if a file contains File(s) in its name, it iterates twice -- the first time for the file and the second time for the summary line. Since the variable numItems is overwritten, it always contains the last value which is the expected number (as the summary line is at the end).
This method requires to produce the output by an echo after the inner loop. This requires delayed environment variable expansion, which is enabled by the setlocal command and actually used by the syntax !numItems! rather than %numItems%. Type set /? for more information on this.
Note that the search string File(s) need to be adapted accordingly on non-English systems.

batch file - counting number of files in folder and storing in a variable

I am very new to this. Please help me
I was trying to write a batch file program to count number of files in a folder and assign that to a variable and display it to verify that it has been stored
please help me with the syntax,
thank you in advance
-VK
I'm going to assume you do not want to count hidden or system files.
There are many ways to do this. All of the methods that I will show involve some form of the FOR command. There are many variations of the FOR command that look almost the same, but they behave very differently. It can be confusing for a beginner.
You can get help by typing HELP FOR or FOR /? from the command line. But that help is a bit cryptic if you are not used to reading it.
1) The DIR command lists the number of files in the directory. You can pipe the results of DIR to FIND to get the relevant line and then use FOR /F to parse the desired value from the line. The problem with this technique is the string you search for has to change depending on the language used by the operating system.
#echo off
for /f %%A in ('dir ^| find "File(s)"') do set cnt=%%A
echo File count = %cnt%
2) You can use DIR /B /A-D-H-S to list the non-hidden/non-system files without other info, pipe the result to FIND to count the number of files, and use FOR /F to read the result.
#echo off
for /f %%A in ('dir /a-d-s-h /b ^| find /v /c ""') do set cnt=%%A
echo File count = %cnt%
3) You can use a simple FOR to enumerate all the files and SET /A to increment a counter for each file found.
#echo off
set cnt=0
for %%A in (*) do set /a cnt+=1
echo File count = %cnt%
#echo off
setlocal enableextensions
set count=0
for %%x in (*.txt) do set /a count+=1
echo %count%
endlocal
pause
This is the best.... your variable is: %count%
NOTE: you can change (*.txt) to any other file extension to count other files.....
The mugume david answer fails on an empty folder; Count is 1 instead of a 0 when looking for a pattern rather than all files. For example *.xml
This works for me:
attrib.exe /s ./*.xml | find /v "File not found - " | find /c /v ""
This might be a bit faster:
dir /A:-D /B *.* 2>nul | find /c /v ""
`/A:-D` - filters out only non directory items (files)
`/B` - prints only file names (no need a full path request)
`*.*` - can filters out specific file names (currently - all)
`2>nul` - suppresses all error lines output to does not count them
To build for statement you should know some details at first.
for /F %%i in ('dir /A:-D /B *.* 2^>nul ^| find /c /v ""') do set "COUNT=%%i"
The example above would work, but if you want to copy paste it into another for-expression - might not.
The for /F ... expression by default has ; character as EOL character and space+tabulation characters as line separators.
If you use a file path as input in for-expression, then you can override these characters:
for /F "eol= delims=" %%i in (";My file path") do echo.Value: %%i
Where the end of eol= might not visible here. It is just a file path invalid not printable character, in this case - code 04. In most consoles and editors (except stackoverflow itself) you can type it as:
press ALT
type 04 from numeric keypad
release ALT
Another issue avoid here is always reset variable you want to use before the for-expression in case if for-expression is not executed:
set FILE=undefined
for /F %%i in (";My file path") do set "FILE=%%i"
echo.FILE=%FILE%
The fastest code for counting files with ANY attributes in folder %FOLDER% and its subfolders is the following. The code is for script in a command script (batch) file.
#for /f %%a in ('2^>nul dir "%FOLDER%" /a-d/b/-o/-p/s^|find /v /c ""') do set n=%%a
#echo Total files: %n%.
Change into the directory and;
attrib.exe /s ./*.* |find /c /v ""
EDIT
I presumed that would be simple to discover. use
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "batchfile.bat";
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
I run this and the variable output was holding this
D:\VSS\USSD V3.0\WTU.USSD\USSDConsole\bin\Debug>attrib.exe /s ./*.* | find /c /v "" 13
where 13 is the file count. It should solve the issue
for /F "tokens=1" %a in ('dir ^| findstr "File(s)"') do echo %a
Result:
C:\MyDir> for /F "tokens=1" %a in ('dir ^| findstr "File(s)"') do #set FILE_COUNT=%a
C:\MyDir> echo %FILE_COUNT%
4 // <== There's your answer
FOR /f "delims=" %%i IN ('attrib.exe ./*.* ^| find /v "File not found - " ^| find /c /v ""') DO SET myVar=%%i
ECHO %myVar%
This is based on the (much) earlier post that points out that the count would be wrong for an empty directory if you use DIR rather than attrib.exe.
For anyone else who got stuck on the syntax for putting the command in a FOR loop, enclose the command in single quotes (assuming it doesn't contain them) and escape pipes with ^.
I have used a temporary file to do this in the past, like this below.
DIR /B *.DAT | FIND.EXE /C /V "" > COUNT.TXT
FOR /F "tokens=1" %%f IN (COUNT.TXT) DO (
IF NOT %%f==6 SET _MSG=File count is %%f, and 6 were expected. & DEL COUNT.TXT & ECHO #### ERROR - FILE COUNT WAS %%f AND 6 WERE EXPECTED. #### >> %_LOGFILE% & GOTO SENDMAIL
)
With a for loop:
FOR /F %i IN ('dir /b /a-d "%cd%" ^| find /v /c "?"') DO set /a count=%i
echo %count%
Without/avoiding for loop:
(dir /b /a-d ^| find /v /c "?") | (set /p myVar=& cmd /c exit /b %myVar%)
set count=%errorlevel%
echo %count%
Tested in Win 10 cmd
Solution
Requires enabling Windows Subsystems For Linux
Ensure directoryToCount is set to the respective directory
SET directoryToCount=C:\Users\
dir %directoryToCount% > contentsOfDir.txt
echo cat contentsOfDir.txt ^| grep File > countFiles.sh
sh countFiles.sh
del countFiles.sh
del contentsOfDir.txt
Explanation
In both bash and batch environments, the output of a command can be redirected to a file using the > operator.
For example, echo hello world > myFile.txt will produce a file named myFile.txt with hello world as its text.
In a bash environment, one can cat the contents of a File and grep a particular line from the file containing a specified pattern.
For example, cat myFile.txt | grep hello will return lines containing hello
If Windows Subsystems for Linux is enabled, then one can execute sh from a Command Prompt to access a linux-like environment.
Therefore, we can solve this by doing the following
Use dir to acquire a list of files in the directory, as well as the number of files
Redirect the output of dir to a file (perhaps named contentsOfDir.txt).
Create a .sh file to grep for File from contentsOfDir.txt
Call the .sh file from command prompt to invoke the grep
Delete the .sh file
Delete contentsOfDir.txt

Resources