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...
I have a file report.txt having comma separated values like (1,2,3,4). I am checking if the file is not blank then assign the 4 variables with values in the file. But the variables are not set. Any help why this is happening?
setlocal enabledelayedexpansion
for /f %%i in ("Report.txt") do set size=%%~zi
if %size% gtr 0 (
for /F "tokens=1-4 delims=," %%A in ("Report.txt") do (
set "var1=%%a"
set "var2=%%b"
set "var3=%%c"
set "var4=%%d"
)
set var
)
echo %var1%
You've enabled delayed expansion, but you aren't using it. In order to use delayed expansion, you need to use !variable! instead of %variable%. Additionally, the variable specified in for loops is case-sensitive, so you either need to set var1 equal to %%A or use %%a as the loop variable.
setlocal enabledelayedexpansion
for /f %%i in ("Report.txt") do set size=%%~zi
if %size% gtr 0 (
for /F "tokens=1-4 delims=," %%a in (Report.txt) do (
set "var1=%%a"
set "var2=%%b"
set "var3=%%c"
set "var4=%%d"
)
set var
)
echo !var1!
setlocal enabledelayedexpansion
for /f %%i in ("Report.txt") do set size=%%~zi
if %size% gtr 0 (
I changed the code as below and it started working. Thank you guys for your time. Appeciate it.
for /F "tokens=1-4 delims=," %%A in (%cd%\Report.txt) do (
set "var1=%%A"
set "var2=%%B"
set "var3=%%C"
set "var4=%%D"
)
set var
)
echo !var1!
Can anybody help me in making the windows batch file to find the substring from the log file. Sample of the log.log file is as below
ID,Date,Time,Description,IP Address,Host Name,MAC Address
10,02/21/14,00:29:45,Assign,172.20.55.50,PC1,123456789AB1,
31,02/21/14,00:29:45,DNS Update,172.20.55.50,PC1,123456789AB1,
10,02/21/14,00:29:45,Assign,172.30.55.50,PC2,123456789AB2,
31,02/21/14,00:29:45,DNS Update,172.30.55.50,PC1,123456789AB2,
10,02/21/14,00:29:45,Assign,172.20.56.60,PC3,123456789AB3,
10,02/21/14,00:29:45,Assign,172.30.55.60,PC4,123456789AB4,
**11,02/21/14,00:30:45,Assign,172.30.55.10,PC2,123456789AB5,**
**11,02/21/14,00:30:46,Assign,172.30.55.10,PC2,123456789AB5,**
**31,02/21/14,00:00:37,DNS Update Failed,172.17.110.13,TAR-CAR-051180L.WTPK.local,-1,**
This is basically DHCP log file. The objective is to count number of New Assign IP requests (whose ID is 10) and number of renewal IP requests (whose ID is 11).
For ID 10, if the IP starts with 172.20.55 or 172.20.56 it should increment in the counter "NewPoolA" and if the IP starts with 172.30.55 or 172.30.56 it should increment in "NewPoolB".
Similarly for ID 11 if the IP starts with 172.20.55 or 172.20.56 it should increment in the counter "RenewPoolA" and if the IP starts with 172.30.55 or 172.30.56 it should increment in "RenewPoolB".
so far what I have done is below
#echo off
Setlocal EnableDelayedExpansion
set /a NewPoolA=0
set /a NewPoolB=0
set /a RenewPoolA=0
set /a RenewPoolB=0
for /F "tokens=1-6 delims=," %%a in (log.log) do (
if %%a equ 10 (
rem if %%e contains 172.20.55 (
set /a NewPoolA += 1
goto someLabel
)
rem else if %%e contains 172.20.56 (
set /a NewPoolA += 1
goto someLabel
)
rem else if %%e contains 172.30.55 (
set /a NewPoolB += 1
goto someLabel
)
rem else if %%e contains 172.30.56 (
set /a NewPoolB += 1
goto someLabel
)
rem -------- if id 10 and not match any condition then
goto someLabel
) else if %%a equ 11 (
rem if %%e contains 172.20.55 (
set /a RenewPoolA += 1
goto someLabel
)
rem else if %%e contains 172.20.56 (
set /a RenewPoolA += 1
goto someLabel
)
rem else if %%e contains 172.30.55 (
set /a RenewPoolB += 1
goto someLabel
)
rem else if %%e contains 172.30.56 (
set /a RenewPoolB += 1
goto someLabel
)
rem -------- if id 11 and not match any condition then
goto someLabel
)
)
echo Total new request in Pool A is %NewPoolA%
echo Total renewal request in Pool A is %RenewPoolA%
echo Total new request in Pool B is %NewPoolB%
echo Total renewal request in Pool B is %RenewPoolB%
This is the logic and my understanding that how it will work. I dont know the syntax of dos batch commands.
These two pools are just example. I have total 80 pools for which i have to do this. In log file there are almost 100,000 entries. To match this number of lines to each of the pool will take too much time. So, the goal is to jump out of the 'if condition' after incrementing the value and to shorten the execution time of the batch file.
One more thing, the log file contains duplicate entries in different timings for same mac address. I need the increment will run only for unique entries of mac address.
Does this do what you need:
#echo off
for /f %%a in ('type "file.csv"^|findstr "^10," ^|findstr ",172.20.55 ,172.20.56"^|find /c /v ""') do set AAA-NewPoolA=%%a
for /f %%a in ('type "file.csv"^|findstr "^10," ^|findstr ",172.30.55 ,172.30.56"^|find /c /v ""') do set AAA-NewPoolB=%%a
for /f %%a in ('type "file.csv"^|findstr "^11," ^|findstr ",172.20.55 ,172.20.56"^|find /c /v ""') do set AAA-ReNewPoolA=%%a
for /f %%a in ('type "file.csv"^|findstr "^11," ^|findstr ",172.30.55 ,172.30.56"^|find /c /v ""') do set AAA-ReNewPoolB=%%a
set aaa
pause
The simplest and fastest way to accumulate results when they are selected via complicated tests is to not identify each one separately, but accumulate all groups of related results and extract the desired ones at end. In your case, this is easily achieved via a two dimensional array this way:
#echo off
setlocal EnableDelayedExpansion
rem Accumulate results for all ID.IP (first 3 groups) combinations
for /F "skip=1 tokens=1,5-7 delims=,." %%a in (log.log) do (
set /A requests[%%a.%%b.%%c.%%d]+=1
)
rem Get desired results
set /A NewPoolA = requests[10.172.20.55] + requests[10.172.20.56]
set /A NewPoolB = requests[10.172.30.55] + requests[10.172.30.56]
set /A RenewPoolA = requests[11.172.20.55] + requests[11.172.20.56]
set /A RenewPoolB = requests[11.172.30.55] + requests[11.172.30.56]
echo Total new request in Pool A is %NewPoolA%
echo Total renewal request in Pool A is %RenewPoolA%
echo Total new request in Pool B is %NewPoolB%
echo Total renewal request in Pool B is %RenewPoolB%
You may also make good use of the flexibility that arrays provide to define your 80 pools in a much simpler way. For example:
#echo off
setlocal EnableDelayedExpansion
rem Accumulate results for all ID.IP (first 3 groups) combinations
for /F "skip=1 tokens=1,5-7 delims=,." %%a in (log.log) do (
set /A requests[%%a.%%b.%%c.%%d]+=1
)
rem Get and show desired results from a long pool definition list
for %%A in ("NewPoolA=10 172.20.55+10 172.20.56"
"NewPoolB=10 172.30.55+10 172.30.56"
"RenewPoolA=11 172.20.55+11 172.20.56"
"RenewPoolB=11 172.30.55+11 172.30.56") do (
for /F "tokens=1-5 delims==+ " %%a in (%%A) do (
set /A %%a=requests[%%b.%%c]+requests[%%d.%%e]
echo Total %%a request is !%%a!
)
)
Output example with your data:
C:\> test
Total NewPoolA request is 2
Total NewPoolB request is 2
Total RenewPoolA request is 0
Total RenewPoolB request is 1
You may even replace the "NewPoolA" message in these results by "new request in Pool A" one using a second array! ;-)
LAST EDIT
This solution has been modified several times because new details not included in the original question. In order to avoid confusions, I removed previous solutions and posted just the final one. The Batch code below accumulate requests for unique MAC addresses only, ignore MAC addresses equal to -1, and allows points in any field besides IP address.
#echo off
setlocal EnableDelayedExpansion
rem Accumulate results for all ID.IP(first 3 groups).MAC_addr combinations
rem NEW: Ignore MAC_addresses equal to -1
for /F "skip=1 tokens=1,5,7 delims=," %%a in (log.log) do (
if "%%c" neq "-1" for /F "tokens=1-3 delims=." %%i in ("%%b") do (
set /A requests[%%a.%%i.%%j.%%k.%%c]+=1
)
)
rem Get and show desired results from a long definition list
rem NEW: Only accumulate requests for unique MAC addresses (count=1)
for %%A in ("NewPoolA=10 172.20.55+10 172.20.56"
"NewPoolB=10 172.30.55+10 172.30.56"
"RenewPoolA=11 172.20.55+11 172.20.56"
"RenewPoolB=11 172.30.55+11 172.30.56") do (
for /F "tokens=1-5 delims==+ " %%a in (%%A) do (
set %%a=0
for /F "tokens=2 delims==" %%x in ('set requests[%%b.%%c 2^>NUL') do (
if %%x equ 1 set /A %%a+=1
)
for /F "tokens=2 delims==" %%x in ('set requests[%%d.%%e 2^>NUL') do (
if %%x equ 1 set /A %%a+=1
)
echo Total %%a request is !%%a!
)
)
Here you go. I'm sure you can work out the rest
#echo off
setlocal
set "NewPoolA=0"
set "NewPoolB=0"
set "RenewPoolA=0"
set "RenewPoolB=0"
for /F "tokens=1-6 delims=," %%a in (log.log) do (
if "%%~a" equ "10" (
for /f %%b in ('echo "%%e"^|Findstr /c:"172.20.55" /c:"172.20.56"') do (
set /a NewPoolA+=1
)
)
)
echo Total new request in Pool A is %NewPoolA%
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
:: remove variables starting $ or #
For %%b IN ($ #) DO FOR /F "delims==" %%a In ('set %%b 2^>Nul') DO SET "%%a="
FOR /f "tokens=1-4delims=. " %%a IN (q21935716.txt) DO (
IF "%%d"=="" (SET $%%a=%%b) ELSE (SET #%%b.%%c.%%d=%%a)
)
FOR /f "tokens=1,5,6,7delims=.," %%a IN (q21935716.log) DO (
IF DEFINED $%%a IF DEFINED #%%b.%%c.%%d (SET /a !$%%a!!#%%b.%%c.%%d!+=1)
)
FOR /f "tokens=2delims==" %%a IN ('set $') DO SET %%a
GOTO :EOF
Here's a reasonably flexible routine, given that you have 80 pools.
The file q21935716.txt contains the following:
10 newpool
11 renewpool
A 172.20.55
A 172.20.56
B 172.30.55
B 172.30.56
Fairly obviously, lines of either ID+poolname or poolsection+IP.
The first step is to clear eny variables that start with either $ or #.
Next step is to read the file q21935716.txt using . or space as delimiters. This means that the poolname lines will have 2 tokens and the ip lines 4. The variables $poolname and #ip are then set up, containing the ID and poolsection respectively.
It's then a matter of reading the logfile, delimiers of . , or space which means tokens 1,5,6,7 are of interest. Only if $ID and #IP both exist is the line of interest. In tose lines, you need to increment the poolnamepoolsection.
The final for simply lists the poolnames.
this is file.txt
hello
hai
So i wanna create a program that will read this file. and then replace every ALPHABET with the succeeding one. keeping all numbers and other symbols intact.
so the file.txt would become
ifmmp
ibj
current have tried reading every \n and then reading every character in the line, but echoing them would result in it being in different lines
ie
for /f "delims=" %%i in ('^<%path% findstr /n "^"') do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:*:=!"
set "num=-1"
:loop
set /a num=num+1
call set "name2=%%line:~%num%,1%%"
if defined name2 (
rem set /a name2+=1 this statement wont work
echo %name2%
goto :loop )
)
but then the output i get is
echo is off
e
l
l
o
any ideas?
The set /a command does only work with numbers, you can try this:
#echo off&setlocal
set "alfa=abcdefghijklmnopqrstuvwxyza"
set "test=hello hai"
set /a num=0
:loop
call set "char=%%test:~%num%,1%%"
if not defined char goto:eof
call set "alf1=%%alfa:*%char%=%%"
if "%char%" neq " " echo %alf1:~0,1%
set /a num+=1
goto:loop
..output is:
i
f
m
m
p
i
b
j
Here is a ROT13 batch file - what you are describing is a ROT1 translation. Maybe this will work for you as it is, or give you ideas and you can modify it.
#echo off
:: by aacini
:: program works as a filter, that is, it read lines from keyboard (stdin) and send output to screen (stdout). "type filetext.txt|rot13.bat" or "rot13.bat < filetext.txt"
:: with d benham update
#echo off
:: simple rot-13 conversion filter program
setlocal disableDelayedExpansion
set lower=abcdefghijklmnopqrstuvwxyz
set upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
for /l %%a in (0,1,25) do (
set /a "rot13=(%%a+13)%%26"
setlocal enableDelayedExpansion
for %%b in (!rot13!) do for /f "tokens=1-4" %%A in (
"!lower:~%%a,1! !lower:~%%b,1! !upper:~%%a,1! !upper:~%%b,1!"
) do (
endlocal
set "lower%%A=%%B"
set "upper%%C=%%D"
)
)
for /f "delims=" %%a in ('findstr /n "^"') do (
set "line=%%a"
setlocal enableDelayedExpansion
set "line=!line:*:=!"
set output=
if defined line (
set /a len=0
for /l %%b in (12,-1,0) do (
set /a "len|=1<<%%b"
for %%c in (!len!) do if "!line:~%%c,1!" equ "" set /a "len&=~1<<%%b"
)
for /l %%b in (0,1,!len!) do (
set "char=!line:~%%b,1!"
if defined lower!char! for /f delims^=^ eol^= %%c in ("!char!") do (
if "!lower:%%c=%%c!" neq "!lower!" (
set "char=!upper%%c!"
) else set "char=!lower%%c!"
)
set "output=!output!!char!"
)
)
echo(!output!
endlocal
)
I want to create a batch file to read every line of a file in a loop and replace a string with another one. Following is my code snippet:
for /F "tokens=*" %%i in (myfile) do (
set str=%%i
set str=%str: %oldstring% = %newstring%%
echo %str% >> newfile
)
This results in a newfile with 'Echo is off' as many lines as there are in myfile. Seems like str variable holds no value at all when assigned to %%i. Can someone help me?
Try out this small script:
#echo off
set val=50
echo %val%
for /l %%i in (1,1,1) do (
set val=%%i
echo %val%
)
echo %val%
pause>nul
The output is:
50
50
1
Not what you expected, right?
That's because in a for loop, variables aren't updated until the loop has finished. To combat this, you can use setlocal enabledelayedexpansion, and replace the percent signs (%) with an exclamation mark (!):
#echo off
setlocal enabledelayedexpansion
set val=50
echo %val%
for /l %%i in (1,1,1,) do (
set val=%%i
echo !val!
)
echo %val%
pause>nul
The output:
50
1
1
The reason the str variable holds no value (during the for loop) is because it hasn't been set beforehand.
So, with these quick modifications, your script will work...
setlocal enabledelayedexpansion
for /f "tokens=*" %%i in (myfile) do (
set str=%%i
set str=!str: %oldstring% = %newstring%!
echo !str! >> newfile
)
By the way, this snippet is assuming that oldstring and newstring won't be set within the forloop, otherwise things will get messy.
Have fun.
having spent some time at this I got the correct way:
#echo off
setlocal enabledelayedexpansion
set oldstring=AF-07295
set /a count=1000
for %%f in (*.*) do (
set /a count=!count!+1
for /f "tokens=*" %%i in (%%f) do (
set str=%%i
call set str=%%str:!oldstring!=!count!%%
echo !str! >> %%~nf.ordnew
)
)
endlocal
setlocal ENABLEDELAYEDEXPANSION
set filein="c:\program files\test1.txt"
set fileout="c:\program files\test2.txt"
set old=#VERSION#
set new=2.0.3
for /f "tokens=* delims=ΒΆ" %%i in ( '"type %filein%"') do (
set str=%%i
set str=!str:%old%=%new%!
echo !str! >> %fileout%
)
working perfect
and isn't removing white spaces at the begining of the lines file