My batch script bad counting occurrences of string in a file - string

#echo off
set /a count = 0
for /f "delims=" %%a in ('dir "%~1" /a:-d /b') do call :next "%%a" "%~2"
echo found %count% occurances of "%~2"
pause
GOTO:EOF
:next
set num=
for /f "delims=" %%b in ('find /c %2 ^< %1') do set num=%%b
set /a count=count+num
My code is wrong count of the text specified in the parameter. What's the problem?

As Mark said, find return the number of lines that match in a file, not the number of individual strings in one line. To do so, you need to use another method, for example:
#echo off
setlocal EnableDelayedExpansion
set /a count = 0
for /f "delims=" %%a in ('dir "%~1" /a:-d /b') do (
for /F "delims=" %%b in ('find %2 ^< "%%a"') do call :next "%%b" "%~2"
)
echo found %count% occurances of "%~2"
pause
GOTO:EOF
:next
set num=0
set "line=%~1"
:nextMatch
set "line2=!line:*%~2=!"
if "!line2!" equ "!line!" goto endMatchs
set /A num+=1
set "line=!line2!"
if defined line goto nextMatch
:endMatchs
set /a count=count+num
For example:
C:> type 1.txt
An example of text file.
This example line have two "example" words.
End of example.
C:> test 1.txt "example"
found 4 occurances of "example"

find will count the number of lines that match your string, so searching 'xyyx' for 'x' will count as one match, even though the x's aren't in a row. If that's not what you want, you'll need a different tool.

Related

Selecting x unique random files from a folder and its subfolders with a batch file

I've got some batch code that selects three random files from a folder (and its subfolders), but it's possible for it to end up selecting the same file more than once. I'd like it to always select unique files.
It creates a temporary text file with all the options, so I've tried to get it to remove the selected line and subtract one from the total file count each time one is selected, so that it's removed from the pool of options.
Here's the whole thing:
#echo off
setlocal
:: Create numbered list of files in a temporary file
set "tempFile=%temp%\%~nx0_fileList_%time::=.%.txt"
set "tempFileTwo=%temp%\%~nx0_fileList2_%time::=.%.txt"
pushd %1
dir /b /s /a-d *.mov *.mp4 | findstr /n "^" >"%tempFile%"
popd
:: Count the files
for /f %%N in ('type "%tempFile%" ^| find /c /v ""') do set cnt=%%N
for /l %%N in (1 1 3) do call :openRandomFile
:: Delete the temp files
del "%tempFile%"
del "%tempFileTwo%"
exit /b
:openRandomFile
set /a "randomNum=(%random% %% cnt) + 1"
for /f "tokens=1* delims=:" %%A in (
'findstr "^%randomNum%:" "%tempFile%"'
) do (
start "" "%%B"
echo Selection: %%B
set /a cnt -= 1
findstr /l /v "%%B" "%tempFile%" > "%tempFileTwo%"
copy /y "%tempFileTwo%" "%tempFile%"
)
exit /b
The part that doesn't work is:
findstr /l /v "%%B" "%tempFile%" > "%tempFileTwo%"
copy /y "%tempFileTwo%" "%tempFile%"
I was hoping my findstr would find the selected file and remove it, and copy everything else over, but it apparently matches everything and copies a totally blank file. Not sure what I'm doing wrong.
The %RANDOM% number may return duplicates, which we have to account for. The following adaptation of your code retries to select a random file when an already chosen one would have to be returned:
#echo off
rem // Explicitly preset configuration:
setlocal EnableExtensions DisableDelayedExpansion
:: Create numbered list of files in a temporary file
set "tempFile=%temp%\%~nx0_fileList_%time::=.%.txt"
set "tempFileTwo=%temp%\%~nx0_fileList2_%time::=.%.txt"
rem // Improve quotation and regard failure if `%1` does not point to a directory:
pushd "%~1" && (
dir /b /s /a-d *.mov *.mp4 | findstr /n "^" > "%tempFile%"
popd
) || del "%tempFile%" 2> nul
:: Count the files
set "cnt=0" & for /f %%N in ('type "%tempFile%" ^| find /c /v ""') do set "cnt=%%N"
rem // Regard case when there are less than three files:
set "num=3" & if %cnt% lss 3 set "num=%cnt%"
for /l %%N in (1 1 %num%) do call :openRandomFile
:: Delete the temp files
del "%tempFile%"
del "%tempFileTwo%"
endlocal
exit /b
:openRandomFile
set /a "randomNum=(%random% %% cnt) + 1"
(for /f "tokens=1* delims=:" %%A in (
'findstr "^%randomNum%:" "%tempFile%"'
) do (
echo Selection: %%B
start "" "%%B"
set /a "cnt -= 1"
rem /* Ensure to match the whole line, including the colon-separated count prefix,
rem and escape `findstr` meta-characters to prevent wrong matches: */
set "LINE=%%A:%%B"
setlocal EnableDelayedExpansion
set "LINE=!LINE:\=\\!" & set "LINE=!LINE:.=\.!"
set "LINE=!LINE:[=\]!" & set "LINE=!LINE:]=\]!"
set "LINE=!LINE:^=\^!" & set "LINE=!LINE:$=\$!"
findstr /V /X /I /C:"!LINE!" "!tempFile!" > "!tempFileTwo!"
endlocal
copy /y "%tempFileTwo%" "%tempFile%"
)) || (
rem /* Detect if `for /F` loop did not iterate, meaning that an already selected
rem file would have become chosen once again, hence retry random selection: */
goto :openRandomFile
)
exit /b
However, I would choose another strategy to get files without duplicates, namely, to build a list of files with each one preceded with a random number, then sorting that list by said numbers and eventually taking the first few elements from the list.
Here is a possible implementation, using a temporary file to build the list:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "_TMPF=%TEMP%\%~nx0_%RANDOM%.lst"
rem // Change into target directory:
pushd "%~1" && (
rem // Write to temporary file:
> "%_TMPF%" (
rem // Build list of matching files with a random number preceded:
for /F "delims=" %%F in ('dir /S /B /A:-D-H-S "*.mov" "*.mp4"') do (
set "FILE=%%F"
setlocal EnableDelayedExpansion
echo/!RANDOM!:!FILE!
endlocal
)
)
rem // Sort list of files by preceding random numbers:
sort "%_TMPF%" /O "%_TMPF%"
rem // Read from temporary file:
< "%_TMPF%" (
rem // Fetch the first three items from the list of files:
setlocal EnableDelayedExpansion
for /L %%I in (1,1,3) do (
set /P FILE="" && (
echo Selection: !FILE:*:=!
start "" "!FILE:*:=!"
)
)
endlocal
)
rem // Return from target directory:
popd
rem // Clean up temporary file:
del "%_TMPF%" 2> nul
)
endlocal
exit /B
This is another possible approach, using variables like $FILES[] that constitute a pseudo-array:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Change into target directory:
pushd "%~1" && (
rem // Clean up (pseudo-)array variable:
for /F "delims==" %%V in ('set "$FILES[" 2^> nul') do set "%%V="
rem /* Build list of matching files in array with random number as indices
rem plus a counter, just to avoid duplicate variable/element names: */
set /A "INDEX=0"
for /F "delims=" %%F in ('dir /S /B /A:-D-H-S "*.mov" "*.mp4"') do (
setlocal EnableDelayedExpansion & for %%R in (!RANDOM!_!INDEX!) do (
endlocal & set "$FILES[%%R]=%%F" & set /A "INDEX+=1"
)
)
rem /* Fetch the first three elements of the array of files, which becomes
rem implicitly sorted by the indices by the `set` command: */
set /A "INDEX=0"
for /F "tokens=1* delims=:=" %%E in ('set "$FILES[" 2^> nul') do (
setlocal EnableDelayedExpansion
if !INDEX! lss 3 endlocal & (
echo Selection: %%F
start "" "%%F"
) else endlocal
set /A "INDEX+=1"
)
rem // Return from target directory:
popd
)
endlocal
exit /B
There's a good chance that
findstr /l /v /C:"%%B" "%tempFile%" > "%tempFileTwo%"
may restore sanity.
If %%B contains spaces, then each individual word in "%%B" will be matched. The /c: instructs findstr to treat the quoted string as a string to match, not a list of substrings.
An easier way to achieve your objective would be:
In your main routine, before the pushd
:: remove variables starting $
FOR /F "delims==" %%b In ('set $ 2^>Nul') DO SET "%%b="
where $ can be any string you might desire, like #opened. This line clears any variable that starts $.
And in :openRandomFile, after setting randomNum,
if defined $%randomNum% goto openRandomFile
set $%randomNum%=Y
which establishes a flag $%randomnum% to prevent the number from being processed more than once.
I'm sure you can work out that adjusting the tempfiles can then be removed - including the decrement of cnt...

Pull Partial File Name as Variable

I am looking for a short .bat script that can pull a partial file name and set it as a variable. The file names will consistently be the following structure, however there maybe more than 1 files at a time:
B06XTJ8RX3.MAIN.PC_430.jpg
or
B06XTJ8RX3.MRG1.PC_430.jpg
While there may be multiple files, the first 10 characters will be the same.
I want to name 'B06XTJ8RX3' as the variable. So far I have only been able to do this by writing the file names up to the first '.' into a text document and then pulling the contents of the text file removing the duplicate strings. All of that seems unnecessary.
set images=C:\file_path\images\
for /F "tokens=1 delims=." %%A IN ('dir /b %images%') do echo %%A >> dupes.txt
type NUL > asins.txt
for /F %%i in ('type dupes.txt ^| sort') Do (
If NOT "!prev!" == "%%i" (echo %%i>>asins.txt)
set prev=%%i
)
type NUL > dupes.txt
set prev=5000
Does anyone have a simple solution for this?
for /f "tokens=2delims==" %%x in (`set # 2^>nul`) do set "%%x="
set images=C:\file_path\images\
for /F "tokens=1 delims=." %%A IN ('dir /b %images%') do set /a "#%%A"+=1
set #
should report
#B06XTJ8RX3=2
for the above filenames (#firstelement=occurrences)
The for /f in the first line is intended to remove all variables starting # from the environment.
With each string detected, increment the count of #string found.
for /f "tokens=1,2delims==" %%x in (`set # 2^>nul`) do if "%%y" neq 1 echo %%x
should report each name with more than 1 file found.
If you guarantee that of the n files in the directory, the first 10 chars will all be the same and the 11th will be . (and therefore there are no files that do not fit this pattern) then
set images=C:\file_path\images\
set /a count=0
for /F "tokens=1 delims=." %%A IN ('dir /b /a-d %images%') do set "name=%%A"&set/a count+=1
echo the %count% files start "%name%"
use the /a-d switch to turn off directorynames, should they exist.
count them as a free bonus.

How to search ocuurence of a string across all files in a directory using batch

I need to search a string "style" across all html files present in a folder.
In the output I need file name and count of the occurrence of the string "style".
for eg output will be like
a.html 3
b.html 6
c.html 7
I have tried using
find /c "style" *.html
The problem is in one line it reads the string only once and moves to next
i.e if a file has content:
style style style style
style
It will read count as 2 only.
I need count as 5 and that too search should be across the folder.
Please help
This should work, as long as the file names does not contain spaces, special Batch characters nor arithmetic operators (like -).
#echo off
setlocal EnableDelayedExpansion
set "word=style"
for /F "tokens=1* delims=:" %%a in ('findstr "%word%" *.html') do (
set "line=%%b"
set "line=!line:"=!"
for %%c in ("!line:%word%=" "!") do set /A "count[%%a]+=1"
set /A "count[%%a]-=1"
)
for /F "tokens=2,3 delims=[]=" %%a in ('set count[') do echo %%a %%b
for /F "delims=" %G in ( 'findstr /S /M /I "style" "%CD%\*.html" ') do #find /c "style" "%~G" | findstr /V /R "^S"

How to delete or skip multiline string in a file using batch files

Ex-
Input file-File1.txt contains-
multiline
String
Mango
Orange
String
Orange
then out put should be in File2.txt-
multiline,
Mango
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "prevline="
(
FOR /f "delims=" %%a IN ('type q22913138.txt^|sort') DO (
IF DEFINED prevline IF "%%a"=="!prevline!" ECHO(%%a
SET "prevline=%%a"
)
)>tempfile.txt
type q22913138.txt|FINDSTR /v /i /x /g:tempfile.txt >newfile.txt
DEL tempfile.txt 2>nul
GOTO :EOF
I used a file named q22913138.txt containing your data for my testing.
Produces newfile.txt
I've made some minor adjustments.
The problem appears to be that your data file has no newline after the last Orange. Unfortunately, it's very difficult to see precisely what the format is on a text post.
for every word in file1 (count occurence of this word and if only one (write to file2)):
#echo off
for /f "delims=" %%i in ("file1.txt") do (
for /f "tokens=2 delims=:" %%n in ('find /c "%%i" "file1.txt"') do (
if %%n equ 1 echo %%i >>"file2.txt"
)
)
edit: ("Is there any optimize way to print out the string which exist more than one times in your file with the number of occurence- input file--file1.txt "
#echo off
echo.>file2.txt
for /f "delims=" %%i in (file1.txt) do (
for /f "tokens=2 delims=:" %%n in ('find /c "%%i" "file1.txt"') do (
if %%n equ 1 (
rem echo %%i >>"file2.txt"
) else (
find "%%i" "file2.txt">nul ||echo %%i occures %%n times >>"file2.txt"
)
)
)

Using DOS batch script: find a line in a properties file and replace text

Trying to replace a string in a properties file on a certain line by using a batch file. I know that this can be done WITHOUT the use of a temp file, as I have seen it before, but forgotten how to do it.
I know that if I have a var.properties file that contains this:
CLASSPATH=bsh.jar;other.jar
VARTEST=dummy
ANOTHERVAR=default
I am trying to update the CLASSPATH value in the .properties file without changing the order of the properties file.
This is a properties file and so I believe the answer would be related to using:
for /f "tokens=1,2* delims==" %%i in (var.properties) do (
#echo Key=%%i Val=%%j
)
Instead of findstr use find with the /v and /i switches on "classpath". This will OMIT returning the line with classpath in it, then you can echo what you want in the file along w/VARTEST=dummy
SET NEWVAL=CLASSPATH=test.jar
SET FILE=think.properties
FOR /F "USEBACKQ tokens=*" %%A IN (`TYPE "%FILE%" ^|FIND /V /I "classpath"`) DO (
ECHO CLASSPATH=%NEWVAL%>>"%FILE%"
ECHO %%A>>"%FILE%"
)
EDIT:
SETLOCAL ENABLEDELAYEDEXPANSION
SET NEWVAL=test.jar
SET OLDFILE=OLD_think.properties
SET NEWFILE=think.properties
SET COUNT=1
MOVE "%NEWFILE%" "%OLDFILE%"
FOR /F "USEBACKQ tokens=*" %%A IN (`TYPE "%OLDFILE%" ^|FIND /C /I "classpath"`) DO (
SET LINE=%%A
)
FOR /F "USEBACKQ tokens=*" %%A IN (`FIND /V "" ^<"%OLDFILE%"`) DO (
IF %COUNT% NEQ %LINE% (ECHO %%A>>"%NEWFILE%") ELSE (ECHO %NEWVAL%>>"%NEWFILE%")
SET /a COUNT=!COUNT!+1
)
Basically states,
rename think.properties to OLD_think.properties
read OLD_think.properties and find the line number with string
"classpath" in it and set it to variable LINE
Find all lines in OLD_think.properties, and echo them into think.properties. once you reach the line where your "CLASSPATH" string existed, it inserts the new line you wanted to replace it with, and everything else stays the same.
I finally broke down and accepted a method using a "temp" file. Using delayed expansion with the '!' character solved my question. Much of this success was due to input by mecaflash .
You can call this script with: CALL Script.bat PropKey NewPropValue Filename
#echo off
:: script for updating property files
SETLOCAL EnableExtensions
SETLOCAL EnableDelayedExpansion
if "%3"=="" (
ECHO Script will optionally accept 3 args: PropKey PropVal File
SET PROPKEY=UseCompression
SET PROPVAL=false
SET FILE=config.properties
) ELSE (
SET PROPKEY=%1
SET PROPVAL=%2
SET FILE=%3
)
FINDSTR /B %PROPKEY% %FILE% >nul
IF %ERRORLEVEL% EQU 1 GOTO nowork
MOVE /Y "%FILE%" "%FILE%.bak"
FOR /F "USEBACKQ tokens=*" %%A IN (`TYPE "%FILE%.bak" ^|FIND /N /I "%PROPKEY%"`) DO (
SET LINE=%%A
)
FOR /F "tokens=1,2* delims=]" %%S in ("%LINE%") DO SET LINE=%%S
SET /A LINE=%LINE:~1,6%
SET /A COUNT=1
FOR /F "USEBACKQ tokens=*" %%A IN (`FIND /V "" ^<"%FILE%.bak"`) DO (
IF "!COUNT!" NEQ "%LINE%" (
ECHO %%A>>"%FILE%"
) ELSE (
ECHO %PROPKEY%=%PROPVAL%>>"%FILE%"
ECHO Updated %FILE% with value %PROPKEY%=%PROPVAL%
)
SET /A COUNT+=1
)
GOTO end
:nowork
echo Didn't find matching string %PROPKEY% in %FILE%. No work to do.
pause
:end

Resources