Windows batch file - How to shuffle and unshuffle files using variable inputs - windows

EDIT: Updated to be more clear and to clarify that I only want to shuffle files 001 to 049 and leave file 050 as 050 and added an "Ideal End Goal Concept" at the end of this post
I'm looking for a way to shuffle 49 files in the current directory with my Windows batch file. All of the files are named .001, .002, etc. all the way to .049 using 9 or 10 numbers entered as variables to the batch file. I need to be able to use the same 9 or 10 numbers entered as variables to be able to "unshuffle" the files back to their original state using a seperate batch file. Any ideas and code examples to do this would be greatly appreciated.
Here is my batch file command line example:
I have 50 files in the folder named testfile.exe.001 to .050 (I only want to shuffle 001 - 049)
shuffle.bat testfile.exe 8 5 2 9 4 1 3 7 6
Making sure that I don't use the same number twice and not in the same place (i.e. don't put "1" as the first variable or it will just rename .001 to .001) so that the first 9 files are shuffled.
I then do something like this:
rem temporarily renaming .001 - .050 files to .001a - .050a to prevent collisions
ren %1.0?? %1.0??a
ren %1.001a %1.00%2
rem which renames testfile.exe.001a to testfile.exe.008
ren %1.002a %1.00%3
rem which renames testfile.exe.002a to testfile.exe.005
ren %1.003a %1.00%4
rem which renames testfile.exe.003a to testfile.exe.002
rem etc. all the way up to %1.009a
Then I figured, I could do the same with files .011 - .019, then with files .021 - 0.29, etc. and that works, but it doesn't shuffle the files across the entire span of 49 files. For example .005 could not become .045, etc.
To unshuffle, I would have to enter the same 9 or 10 number variables into the unshuffler batch file which just does everything in reverse. So without those 9 or 10 numbers (which act as a sequence or 'kind of' password), the files could not be restored to their original names.
Is there a way to improve my code or a better way to shuffle these 50 files using batch file variables (numbers) which would cause a shuffle across the entire span of 49 files and be totally reversible using the same 9 or 10 numbers as variables? Thanks for any help!
Ideal End Goal Concept:
To be able to shuffle these 49 files with all of these being true:
Ability to input a code on the command line (varible(s)) which uniquely affects and is the key to the shuffling order and allows unshuffling
Every file's original order has been changed to another order
No two files from the original order are one after the other in the shuffled order
All files can be shuffled across the entire 001-049 set (e.g. 005 can become 045, 032 can become 016, etc.)

setlocal enabledelayedexpansion
set "sourcedir=c:\testso"
set "filename=%1"
:: clear any variables starting #
for /f "tokens=1delims==" %%e in ('set # 2^>nul') do set "%%e="
:: number of files
set /a #files=8
set /a #files1k=1000+#files
set "parm= %* "
set /a #count=0
set /a #randomadd=0
call :readparms %parm%
:: if 0 or 1 parameter, shuffle randomly
if %#count% lss 2 goto shuffle
:: unshuffling
echo unshuffling
call :shuffleren
goto :eof
:shuffle
set /a #remain=#files1k
for /L %%e in (1001,1,%#remain%) do set "#%%e="
:another
set /a #choice=((%random%+#randomadd) %% #files) + 1001
if defined #%#choice% goto another
if %#remain%%#choice%==10011001 goto shuffle
if %#choice% equ %#remain% goto another
set /a #%#choice%=%#remain%
if %#remain%==1001 goto doneshuffle
set /a #remain-=1
goto another
:doneshuffle
set "#report="
set #lastnum=0
for /L %%e in (1001,1,%#files1k%) do (
set /a #num=!#%%e!-1000
set /a #lastnum+=1
if !#num!==!#lastnum! goto shuffle
set /a #lastnum=#num
set "#report=!#report! !#num!
)
call :shuffleren
echo sequence=%#report:~1%
goto :eof
:shuffleren
for /L %%e in (1001,1,%#files1k%) do (
set "#oldname=%%e"
set "#newname=!#%%e!"
ren "%sourcedir%\%filename%*.!#oldname:~-3!" "%filename%*.!#oldname:~-3!.!#newname:~-3!.t"
)
for %%b in ("%sourcedir%\%filename%*.t") do ren "%%b" "%%~nb"
goto :eof
:readparms
shift
set "parm=%1"
if not defined parm goto :eof
set "parm=9%1"
for /L %%e in (0,1,9) do set "parm=!parm:%%e=!"
if defined parm echo ignored "%1" &goto readparms
set /a #randomadd=%1
set /a parm=1000+%1
set /a #count+=1
set /a #count1k=1000+#count
set /a #%parm%=#count1k
goto readparms
We start with setting the directory to examine and read the filename from the parameter list; then clear any variables starting #.
Establish the number of files to manipulate - I've seen the revised spec. that this should be 49, not 50. I used 8 for testing.
Calculate 1000+#files to save repeatedly calculating. Initialise the count of numeric parameters found and read the parameters from the command line using :readparms. I had some control options to facilitate testing, so added a space either end of the passed parameters list %*.
Having read and stored the parameters,examine #count - 0 or 1 means to shuffle, more means unshuffle.
So, shuffling :
First, set all variables #1001 to #1050 (it's self-adjusting to #files specified) to nothing.
Next, generate a random number 1001..1050 and if it's not already assigned, assign the remaining number from the range 1001..1050 to #the randomnumber and decrement the number remaining until it reaches 1001 (last one assigned). If both #count and #remain are 1001 then we're attempting to rename .001 to .001 so we start again. If #remain and #choice are the same otherwise, choose another number
When we're done shuffling, we need to build the unshuffle string. We grab each number in turn from #1000 to #1050, convert it to a natural number (no leading zeroes) and compare it against the last number processed - if the difference is 1, then we're attempting to resequence 2 sequential filenumbers, and that's not allowed - so restart the shuffling.
Finally, we have a set of variables such as
#1001=1004
#1002=1003
#1003=1002
#1004=1001
which means that file.001 will become .004 ,etc.
So, execute :shuffleren to rename the files.
I chose to rename the files by appending an extra 2 extensions (the new name and a t) then remove the t. This shows a trace of the old and new names (filename.002 becomes filename.002.003 in the above). Changing "%filename%*.!#oldname:~-3!.!#newname:~-3!.t" to "%filename%*.!#newname:~-3!.t"would change filename.002 to filename.003.
Finally, the unshuffle mechanism relies on a string of space-separated numeric arguments that matches the list printed by the shuffle mechanism. It's reaaly just more of the same, but unchecked so providing duplicate or insufficient numbers will no doubt cause a mess. Build a table like the above from the parameters and call the
:shuffleren routine to change the numbers back.
Of course, you should test the routine in a dummy area - don't apply it directly to live files.
Since the random function is seeded from the time, I've attempted to apply some variability by adding into the number-choosing mechanism a number that may be specified on the command line, hence
0 or 1 numeric arguments - if one numeric argument is provided, that argument is added to the random number so you can have a little bit of variation if you happen to run the routine at the same time on two days.
---- revision
#echo off
setlocal enabledelayedexpansion
set "sourcedir=c:\testso"
set "filename=%1"
:: clear any variables starting #
for /f "tokens=1delims==" %%e in ('set # 2^>nul') do set "%%e="
:: number of files
set /a #files=50
set /a #files1k=1000+#files
:: length of code to generate
set /a #lencode=10
set "parm= %* "
set /a #count=0
set /a #randomadd=0
set /a #assigned=0
call :readparms %parm%
:: if 0 or 1 parameter, shuffle randomly
if %#count% gtr 2 goto extendkey
:shuffle
set "#shuffle=y"
for /L %%e in (1,1,%#files%) do set "#%%e="
set "#report="
set /a #assigned=0
: nextrandom
call :assignrandom
if %#assigned% lss %#lencode% goto nextrandom
set "#key=%#report%"
:: Extend the key currently in #Report
:extendkey
set /a #accum=1
:nextmore
call :addmore %#report%
if %#assigned% equ %#files% goto donerandom
if %#accum% neq 10 goto nextmore
:: attempt to assign all unassigned
for /L %%e in (%#files,-1,1) do (
set /a #choice=%%e-1
call :addreport
)
if %#assigned% neq %#files% if defined #shuffle goto shuffle
if %#assigned% neq %#files% echo Fail&goto :eof
:donerandom
if defined #shuffle echo key is %#key%
: now build rename-list
for /L %%e in (1000,1,%#files1k%) do set "#%%e="
set /a #position=1000
:brlloop
set /a #position+=1
set /a #value=%#report% 2>nul
set /a #value+=1000
if defined #shuffle (set /a #%#position%=#value
) else set /a #%#value%=#position
set "#report=%#report:* =%"
if defined #report goto brlloop
:shuffleren
for /L %%e in (1001,1,%#files1k%) do (
set "#oldname=%%e"
set "#newname=!#%%e!"
ren "%sourcedir%\%filename%*.!#oldname:~-3!" "%filename%*.!#oldname:~-3!.!#newname:~-3!.t"
)
for %%b in ("%sourcedir%\%filename%*.t") do ren "%%b" "%%~nb"
goto :eof
:readparms
shift
set "#choice=%1"
if not defined #choice goto :eof
set "#choice=9%1"
for /L %%e in (0,1,9) do set "#choice=!#choice:%%e=!"
if defined #choice echo ignored "%1" &goto readparms
set /a #randomadd=%1
set /a #count+=1
set /a #choice=%1-1
call :addreport
goto readparms
:: Assign a new random number to #report
:assignrandom
set /a #choice=%random%+#randomadd
:: Caution - flow-through
:: Add and count an entry into #report
:: don't add if #choice is already in #report or #assigned
:addreport
set /a #choice=(#choice %% #files) + 1
set /a #assigned+=1
for %%y in (%#assigned% %#report%) do if %#choice%==%%y set /a #assigned-=1&goto :eof
set "#report=%#report%%#choice% "
goto :eof
:: Add some more numbers derived from the existing list
:addmore
set /a #accum+=1
:movel
if "%1"=="" goto :eof
set /a #choice=0
for /L %%y in (1,1,%#accum%) do call set /a #choice=#choice+%%%%y 2>nul
call :addreport
shift
goto movel
This code limits the "key" to #lencode.
First step is to process the parameters provided, but this time build each parameter into #report (the name is a hangover from earlier processing)
If there are fewer than 2 parameters provided, then this is a suffle operation and #shuffle is set. In this case, #lencode random numbers are added to a new #report. If #shuffle is not set, then the key has been provided in the command line and has been assigned to #report.
Using #report as a basis, a specific function is applied, adding new numbers. This means that the result in #report will be identical for the two cases (shuffling or unshuffling) All that is needed then is to add 1000 to the numbers for substringing purposes and build the #1xxx array; either for shuffling or unshuffling; then run the renaming code as before.
If an incorrect code is supplied, then the unshuffling will cause chaos. The file renaming trace-for-verification system survives from the earlier code.
-- another revision
#echo off
setlocal enabledelayedexpansion
set "sourcedir=c:\testso"
set "filename=%1"
:: clear any variables starting #
for /f "tokens=1delims==" %%e in ('set # 2^>nul') do set "%%e="
:: number of files
set /a #files=50
set /a #files1k=1000+#files
:: length of code to generate
set /a #lencode=10
set "parm= %* "
:: Set flag #shuffle
for %%e in (shuffle) do (
if "!parm!" neq "!parm:/%%e=!" set "#%%e=Y"&set "parm=!parm:/%%e=!"
)
set /a #count=0
set /a #randomadd=0
set /a #assigned=0
call :readparms %parm%
:: if 0 or 1 parameter, shuffle randomly
if %#count% gtr 2 goto extendkey
:shuffle
set "#shuffle=y"
for /L %%e in (1,1,%#files%) do set "#%%e="
set "#report="
set /a #assigned=0
: nextrandom
call :assignrandom
if %#assigned% lss %#lencode% goto nextrandom
set "#key=%#report%"
:: Extend the key currently in #Report
:extendkey
set /a #accum=1
:nextmore
call :addmore %#report%
if %#assigned% equ %#files% goto donerandom
if %#accum% neq 10 goto nextmore
:: attempt to assign all unassigned
for /L %%e in (%#files,-1,1) do (
set /a #choice=%%e-1
call :addreport
)
if %#assigned% neq %#files% if defined #shuffle goto shuffle
if %#assigned% neq %#files% echo Fail&goto :eof
:donerandom
if defined #shuffle if defined #key echo key is %#key%
: now build rename-list
for /L %%e in (1000,1,%#files1k%) do set "#%%e="
set /a #position=1000
:brlloop
set /a #position+=1
set /a #value=%#report% 2>nul
set /a #value+=1000
if defined #shuffle (set /a #%#position%=#value
) else set /a #%#value%=#position
set "#report=%#report:* =%"
if defined #report goto brlloop
:shuffleren
for /L %%e in (1001,1,%#files1k%) do (
set "#oldname=%%e"
set "#newname=!#%%e!"
ren "%sourcedir%\%filename%*.!#oldname:~-3!" "%filename%*.!#oldname:~-3!.!#newname:~-3!.t"
)
for %%b in ("%sourcedir%\%filename%*.t") do ren "%%b" "%%~nb"
goto :eof
:readparms
shift
set "#choice=%1"
if not defined #choice goto :eof
set "#choice=9%1"
for /L %%e in (0,1,9) do set "#choice=!#choice:%%e=!"
if defined #choice echo ignored "%1" &goto readparms
set /a #randomadd=%1
set /a #count+=1
set /a #choice=%1-1
call :addreport
goto readparms
:: Assign a new random number to #report
:assignrandom
set /a #choice=%random%+#randomadd
:: Caution - flow-through
:: Add and count an entry into #report
:: don't add if #choice is already in #report or #assigned
:addreport
set /a #choice=(#choice %% #files) + 1
set /a #assigned+=1
for %%y in (%#assigned% %#report%) do if %#choice%==%%y set /a #assigned-=1&goto :eof
set "#report=%#report%%#choice% "
goto :eof
:: Add some more numbers derived from the existing list
:addmore
set /a #accum+=1
:movel
if "%1"=="" goto :eof
set /a #choice=0
for /L %%y in (1,1,%#accum%) do call set /a #choice=#choice+%%%%y 2>nul
call :addreport
shift
goto movel
This time, you need to add a switch /shuffle if you are providing a key and wish to shuffle. Without the /shuffle switch, specifying fewer than 2 numeric arguments will shuffle randomly, more than 2, unshuffle.
Note that #key will only be established for a random shuffle, so that is the only time it's reported (otherwise, the key was provided or it's unshuffling).

Your question is not clear. However, I "half-understood" the part with 9 files, so here it is my partial solution.
This is shuffle.bat:
#echo off
setlocal
set "basefile=%1"
set "i=0"
md "renamed"
:loop
shift
if "%1" equ "" goto endLoop
set /A i+=1
move %basefile%.00%i% renamed\%basefile%.00%1 > NUL
goto loop
:endLoop
move renamed\*.* . > NUL
rd renamed
I started with this set of files that have the same number of characters of their names, so we can always identify them (even with the names changed):
09/09/2022 09:36 a. m. 1 testfile.exe.001
09/09/2022 09:36 a. m. 2 testfile.exe.002
09/09/2022 09:36 a. m. 3 testfile.exe.003
09/09/2022 09:36 a. m. 4 testfile.exe.004
09/09/2022 09:36 a. m. 5 testfile.exe.005
09/09/2022 09:36 a. m. 6 testfile.exe.006
09/09/2022 09:36 a. m. 7 testfile.exe.007
09/09/2022 09:36 a. m. 8 testfile.exe.008
09/09/2022 09:36 a. m. 9 testfile.exe.009
After this command is executed:
shuffle.bat testfile.exe 8 5 2 9 4 1 3 7 6
... you get these files:
09/09/2022 09:36 a. m. 6 testfile.exe.001
09/09/2022 09:36 a. m. 3 testfile.exe.002
09/09/2022 09:36 a. m. 7 testfile.exe.003
09/09/2022 09:36 a. m. 5 testfile.exe.004
09/09/2022 09:36 a. m. 2 testfile.exe.005
09/09/2022 09:36 a. m. 9 testfile.exe.006
09/09/2022 09:36 a. m. 8 testfile.exe.007
09/09/2022 09:36 a. m. 1 testfile.exe.008
09/09/2022 09:36 a. m. 4 testfile.exe.009
That is, the first file go to position 8, the second file to position 5, etc. until the ninth file in position 6. Good!
Now, this is unshuffle.bat:
#echo off
setlocal
set "basefile=%1"
set "i=0"
md "renamed"
:loop
shift
if "%1" equ "" goto endLoop
set /A i+=1
move %basefile%.00%1 renamed\%basefile%.00%i% > NUL
goto loop
:endLoop
move renamed\*.* . > NUL
rd renamed
So after this command is executed:
unshuffle.bat testfile.exe 8 5 2 9 4 1 3 7 6
... the files recover their original positions! :)
Whats next?
New method added
I read again the question, particularly the "I could do the same with files .011 - .019, then with files .021 - 0.29, etc." part and I think I have a complete solution now.
I didn't created two .bat files for shuffle/unshuffle operations. Instead, I added the /U switch as the first parameter of the only batch file to select the "unshuffle" task.
I used two different shuffle methods for each group of ten files. Files in 001..009, 021..029 and 041..049 (even) groups use the original method, and files in 011..019 and 031..039 (odd) groups use the "opposite" method. The files 010, 020, 030, 040 and 050 are not renamed.
This is the new shuffle.bat:
#echo off
setlocal
set "unshuffle="
if /I "%1" equ "/U" set "unshuffle=1" & shift
set "basefile=%1"
set "i=0"
md renamed
:loop
shift
if "%1" equ "" goto endLoop
set /A i+=1
set "flipFlop="
(for /L %%j in (0,1,4) do (
if not defined flipFlop (
if not defined unshuffle (
move %basefile%.0%%j%i% renamed\%basefile%.0%%j%1
) else (
move %basefile%.0%%j%1 renamed\%basefile%.0%%j%i%
)
set "flipFlop=1"
) else (
if not defined unshuffle (
move %basefile%.0%%j%1 renamed\%basefile%.0%%j%i%
) else (
move %basefile%.0%%j%i% renamed\%basefile%.0%%j%1
)
set "flipFlop="
)
)) > NUL
goto loop
:endLoop
echo These files were not renamed:
echo/
dir %basefile%.*
move renamed\*.* . > NUL
rd renamed
After this command:
shuffle.bat testfile.exe 8 5 2 9 4 1 3 7 6
... the resulting files are these:
09/09/2022 11:46 a. m. 6 testfile.exe.001
09/09/2022 11:46 a. m. 3 testfile.exe.002
09/09/2022 11:46 a. m. 7 testfile.exe.003
09/09/2022 11:46 a. m. 5 testfile.exe.004
09/09/2022 11:46 a. m. 2 testfile.exe.005
09/09/2022 11:46 a. m. 9 testfile.exe.006
09/09/2022 11:46 a. m. 8 testfile.exe.007
09/09/2022 11:46 a. m. 1 testfile.exe.008
09/09/2022 11:46 a. m. 4 testfile.exe.009
09/09/2022 11:46 a. m. 10 testfile.exe.010
09/09/2022 11:46 a. m. 18 testfile.exe.011
09/09/2022 11:46 a. m. 15 testfile.exe.012
09/09/2022 11:46 a. m. 12 testfile.exe.013
09/09/2022 11:46 a. m. 19 testfile.exe.014
09/09/2022 11:46 a. m. 14 testfile.exe.015
09/09/2022 11:46 a. m. 11 testfile.exe.016
09/09/2022 11:46 a. m. 13 testfile.exe.017
09/09/2022 11:46 a. m. 17 testfile.exe.018
09/09/2022 11:46 a. m. 16 testfile.exe.019
09/09/2022 11:46 a. m. 20 testfile.exe.020
09/09/2022 11:46 a. m. 26 testfile.exe.021
. . .
09/09/2022 11:46 a. m. 44 testfile.exe.049
09/09/2022 11:46 a. m. 50 testfile.exe.050
After this command:
shuffle.bat /U testfile.exe 8 5 2 9 4 1 3 7 6
files recover their original positions.

Related

Increase amount of numbers for picking randomly

There is a batch file that looks for Pen=n in the list.txt and changes its value (n) randomly from the given row of ten numbers (set "var[pen]=1 2 3 4 5 6 7 8 9 10"). But if there are more than ten numbers say twenty (set "var[pen]=1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20") or hundred, it is still picking a number from first ten ones ignoring the rest.
#echo off
setlocal EnableDelayedExpansion
set "file=D:\list.txt"
set "temp=D:\temp.txt"
set "var[pen]=1 2 3 4 5 6 7 8 9 10" &
for /L %%i in (1,10,1%time:~-2%) do set "rand=!random!"
(for /F "usebackq tokens=1,2 delims==" %%a in ("%file%") do (
if defined var[%%a] (
call :getRandomValue var="!var[%%a]!"
echo %%a=!var!
) else if "%%b" neq "" (
echo %%a=%%b
) else (
echo %%a
)
)) > "%temp%"
move /Y "%temp%" "%file%"
pause > nul && pause > nul
goto :EOF
:getRandomValue value="list"
set /A "rand=!random:~-1!+1"
for /F "tokens=%rand%" %%v in (%2) do set "%1=%%v"
Any help would be appreciated.
edit:
the list.txt contains a list of stuff like pen paper rubber etc. with the corresponding value next to it.
pen=5
pencil=43
paper=0
rubber=22
what the bat file does is just putting random number picking in up from the row of numbers provided. in the following case it would change the value of pen to a random number from 1 to 10. but if I add some extra numbers more than ten it will then just ignore them.
You forgot to specify the origin post of this problem. In such a post, there is not any specification about how the probabilities must be given, but all the 3 examples provided have 10 probable values, so I assumed that all probabilities have 10 values. The fix is add the number of probabilities each value can have:
#echo off
setlocal EnableDelayedExpansion
rem Define the probabilities for new values
set "value[apple]=0 0 0 0 0 0 0 1 1 1" & set "num[apple]=10" & rem 0 (70%) or 1 (30%)
set "value[peach]=0 0 0 0 0 1 2 3 4 5" & set "num[peach]=10" & rem from 0 (50%) to 5 (the rest 50%)
set "value[banana]=54 68 82 96" & set "num[banana]=4" & rem each 25%
rem Randomize
for /L %%i in (1,10,1%time:~-2%) do set "ran=!random!"
(for /F "tokens=1,2 delims==" %%a in (input.ini) do (
if defined value[%%a] (
call :getRandomValue value=%%a
echo %%a=!value!
) else if "%%b" neq "" (
echo %%a=%%b
) else (
echo %%a
)
)) > output.ini
move /Y output.ini input.ini
goto :EOF
:getRandomValue value=item
set /A "ran=%random%%%num[%2]+1"
for /F "tokens=%ran%" %%v in ("!value[%2]!") do set "%1=%%v"
exit /B
You also forget (again) to provide the real format of the input file, that include [headers]:
[Berries]
Strawberry=1
Blackberry=-13
Blueberry=100
Cherry=6
[Fruits]
apple=0
peach=4
banana=18
orange=-2.5
[Vegetables]
Potato=44
Tomato=2
Onion=0
Garlic=17
EDIT 2022/01/08: New method added as requested in comment
As I already said, you have not specified the rules to define the probable values. I proposed a method that works correctly based on your first example, but then you define values that does not conform with my proposed method (10 possible values, like in your first example). I modified the method and then you invented values that does not conform either: "Why 100 values?" "Because any number above 31 will make the method fail..."
I modified the method (again) so you can define the probabilities via value:percent pairs. Here it is:
#echo off
setlocal EnableDelayedExpansion
rem Define the probabilities for new values as value:percent pairs
set "value[apple]=23:17 68:83" & rem 23 at 17%, 68 at 83%
set "value[peach]=0:50 1:10 2:10 3:10 4:10 5:10" & rem from 0 (50%) to 5 (the rest 50%)
set "value[banana]=54:25 68:25 82:25 96:25" & rem each 25%
rem Randomize
for /L %%i in (1,10,1%time:~-2%) do set "ran=!random!"
(for /F "tokens=1,2 delims==" %%a in (input.ini) do (
if defined value[%%a] (
call :getRandomValue %%a
echo %%a=!value!
) else if "%%b" neq "" (
echo %%a=%%b
) else (
echo %%a
)
)) > output.ini
move /Y output.ini input.ini
goto :EOF
:getRandomValue item
set /A "ran=%random%%%100+1, val=0"
for %%a in (!value[%1]!) do for /F "tokens=1,2 delims=:" %%x in ("%%a") do (
if %ran% gtr !val! set "value=%%x"
set /A val+=%%y
)
exit /B
What you are aiming to do is to randomly index from a list, which requires you to first determine the number of items in the list.
one method of doing so:
#Echo off
Set "var[pen]=a b c d e f g h i"
Rem enable enviroment for !expanison!
Setlocal EnableDelayedExpansion
Rem Build 'array', splitting string on spaces and incrementing array size count
Set _i=1& Set "item[1]=%var[pen]: =" & Set /A "_i+=1" & Set "item[!_i!]=%"
Rem index randomly from known array size [one indexed]
For /f delims^= %%i in ('Set /A !random! %%!_i! + 1')Do Echo(Item:%%i = !Item[%%i]!
Pause

How do I generate a seven digit random number which is itself divisible by seven

I am working on a program, and all of the parts work perfectly except one, that part is supposed to generate a seven digit random number which is divisible by 7.
I am aware that there are similiar questions to mine, but I did not find my anwser within them, and despite trying I was myself only capable of somethimes generating such a number.
Any idea how to do so?
You can use the modulo function (see set /?):
#echo off
setlocal
set "number=%random%%random%%random%%random%%random%%random%%random%"
set "number=%number:~0,7%"
set /a remainder=number %% 7
if %remainder% equ 0 (
echo %number% is a multiple of 7
) else (
echo %number% divided by 7 gives a rest of %remainder%
)
Just in case, leading zero(s) is/are ok or even desired as a possibility:
#echo off
setlocal
set "number=%random%%random%%random%%random%%random%%random%%random%"
set "number=%number:~-7%"
set /a remainder=7%number% %% 7
if %remainder% equ 0 (
echo %number% is a multiple of 7
) else (
echo %number% divided by 7 gives a rest of %remainder%
)
Here's a slightly different method, which is designed to propagate a variable, named D7 with a seven digit value, which when divided by seven will return an exact integer:
This first example allows for leading 0's in the resulting value.
Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "D7="
For /L %%G In (0 1 9) Do For %%H In (
%Random:~-1%%Random:~-1%%Random:~-1%%Random:~-1%%Random:~-1%%Random:~-1%
) Do (Set /A D7 = 7%%H%%G %% 7
SetLocal EnableDelayedExpansion & If !D7! Equ 0 (EndLocal
Set "D7=%%H%%G"
GoTo :Next))
:Next
Set D7
Pause
Please note: It is possible that the resulting value could be 0000000 because leading 0's are allowed and 0 is divisible by every integer.
If you do not want to have leading 0's then a very small adaptation is needed:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "D7="
For /L %%G In (1 1 9) Do For %%H In (
%Random:~-1%%Random:~-1%%Random:~-1%%Random:~-1%%Random:~-1%%Random:~-1%
) Do (Set /A D7 = %%G%%H %% 7
SetLocal EnableDelayedExpansion & If !D7! Equ 0 (EndLocal
Set "D7=%%G%%H"
GoTo :Next))
:Next
Set D7
Pause

Find and replace several lines of text with an Batch File?

I have parameter files (text files) which are always filled with different values. It's approx. 1500 Lines. Between this lines is a set of 30 lines which is always the same and this I would like to manipulate with batch file.
So for example.
This is the current file.
1 different value
2 different value
3 different value
4 different value
5 fixed value
6 fixed value
7 fixed value
8 different value
9 different value
But I would like to exchange it like.
1 different value
2 different value
3 different value
4 different value
5 EXCHANGED value
6 EXCHANGED value
7 EXCHANGED value
8 different value
9 different value
Perhaps also to add more text because its not always on the same position. The Text which get exchanged is always the same, but the position in the rows can move a little.
--
#echo off set "replace=something" set "replaced=different"
set "source=Source.txt" set "target=Target.txt"
setlocal enableDelayedExpansion ( for /F "tokens=1* delims=:" %%a in
('findstr /N "^" %source%') do ( set "line=%%b" if defined line set
"line=!line:%replace%=%replaced%!" echo(!line!
I found also already some code which i modifyed, but this works only for single words. If i try to do more he simply ignores the ones before.
I worked again a bit on it..
But its not really doing what i want.
Source_File ; the file where i the text inside what the batch is should looking for (exact)
Traget_File ; the file where what i want in the end with the exchanged lines. (final)
Search_For ; This is a line inside where the starts to exchange.
Exchange_With ; the file with the new Lines.
Dont be made about the not proper working progress bar, i simply like it :P
SET "Source_File=D:\SEARCH.para"
SET "Target_File=D:\A.para"
SET "Search_For=//______________DATA_______________"
SET "Exchange_With=D:\TARGET.para"
set i=10
(Set /P j=...Please wait) < NUL
ECHO.
(Set /P j=..............................) < NUL
:Start
call :DisplayProgressBar %i%
set /a i = i + 1
if /i %i% leq 100 goto start
ECHO.
ECHO.
IF NOT DEFINED Search_For (ECHO Failure: Variable Search_For not set!&GOTO :eof)
IF EXIST %Target_File% (DEL /f %Target_File% 1>NUL 2>NUL)
FOR /f "delims=" %%i IN ('FINDSTR . "%Source_File%"') DO (
SET Line=%%i& CALL :Exchange !Line!
)
GOTO :NEXT
:Exchange
SET Line=!Line:%Search_For%=%Exchange_With%!
IF [!Line!] EQU [] (ECHO.>>%Target_File%) ELSE (ECHO !Line!>>%Target_File%)
GOTO :eof
:NEXT
del "%Source_File%"
move /y "%Target_File%" "%Source_File%"
:DisplayProgressBar
(Set /P j=.) < NUL
title %1%% Completed
exit /b

String processing in windows batch files: How to pad value with leading zeros?

in a Windows cmd batch file (.bat), how do i pad a numeric value, so that a given value in the range 0..99 gets transformed to a string in the range "00" to "99". I.e. I'd like to having leading zeros for values lower than 10.
There's a two-stage process you can use:
REM initial setup
SET X=5
REM pad with your desired width - 1 leading zeroes
SET PADDED=0%X%
REM slice off any zeroes you don't need -- BEWARE, this can truncate the value
REM the 2 at the end is the number of desired digits
SET PADDED=%PADDED:~-2%
Now PADDED holds the padded value. If there's any chance that the initial value of X might have more than 2 digits, you need to check that you didn't accidentally truncate it:
REM did we truncate the value by mistake? if so, undo the damage
SET /A VERIFY=1%X% - 1%PADDED%
IF NOT "%VERIFY%"=="0" SET PADDED=%X%
REM finally update the value of X
SET X=%PADDED%
Important note:
This solution creates or overwrites the variables PADDED and VERIFY. Any script that sets the values of variables which are not meant to be persisted after it terminates should be put inside SETLOCAL and ENDLOCAL statements to prevent these changes from being visible from the outside world.
If you are confident that the number of digits in your original number is always <= 2, then
set "x=0%x%"
set "x=%x:~-2%"
If the number may exceed 2 digits, and you want to pad to 2 digits, but not truncate values larger then 99, then
setlocal enableDelayedExpansion
if "%x%" equ "%x:~-2%" (
set "x=0%x%"
set "x=!x:~-2!"
)
Or without delayed expansion, using an intermediate variable
set paddedX=0%x%
if "%x%" equ "%x:~-2%" set "x=%paddedX:~-2%"
The nice thing about the above algorithms is it is trivial to extend the padding to any arbitrary width. For example, to pad to width 10, simply prepend with 9 zeros and preserve the last 10 characters
set "x=000000000%x%"
set "x=%x:~-10%"
TO prevent truncating
set paddedX=000000000%x%
if "%x%" equ "%x:~-10%" set "x=%paddedX:~-10%"
The single line
IF 1%Foo% LSS 100 SET Foo=0%Foo%
will get you what you want for numbers in the range that you specify. It does not change values in the subset 0-9 if they are already single-padded.
Previous answers had explained all the existent methods to pad a value with left zeros; I just want to add a small trick I used to do that in an easier way. What had not been enough mentioned in previous answers is that in most cases, the value that will be padded is incremented inside a loop and that the padded value is just used to display it (or similar tasks, like renames). For example, to show values from 00 to 99:
set x=0
:loop
rem Pad x value, store it in padded
set padded=0%x%
set padded=%padded:~-2%
rem Show padded value
echo %padded%
set /A x+=1
if %x% leq 99 goto loop
If this is the case, the value of the variable may be used for both control the loop and display its padded value with no modification if its limits are appropriately translated. For example, to show values from 00 to 99:
set x=100
:loop
rem Show padded value
echo %x:~-2%
set /A x+=1
if %x% leq 199 goto loop
This method works also with any number of left zeros to pad.
Antonio
OK GUYS i have found a solution, compressing it down as simple as possible.
#echo off
title pad numbers
set num=0
set zero= 000
:loop
#set /a num=%num%+1
if /i %num% GTR 9 set zero= 00
if /i %num% GTR 99 set zero= 0
if /i %num% GTR 999 set zero=
echo %zero%%num%
goto loop
this will display your count up number using 4 digits. but the code can be altered to use 2 digits as shown below.
#echo off
title pad numbers
set num=0
set zero= 0
:loop
#set /a num=%num%+1
if /i %num% GTR 9 set zero=
echo %zero%%num%
goto loop
if you want to set it as a displayable single variable...
#echo off
title pad numbers
set num=0
set zero= 0
:loop
#set /a num=%num%+1
if /i %num% GTR 9 set zero=
set %zero%%num%=number
echo %number%
goto loop
if you want it to count up in seconds...
#echo off
title pad numbers
set num=0
set zero= 0
:loop
#set /a num=%num%+1
if /i %num% GTR 9 set zero=
set %zero%%num%=number
echo %number%
ping localhost -n 2 >nul
goto loop
i hope this was a great help ^^
This example uses a for loop to demonstrate, but the logic is the same even if you were to use it without the loop. Just echo a 0 in front if the number is less than 10.
setlocal enabledelayedexpansion
for /l %%a in (1,1,40) do (
set n=%%a
if !n! lss 10 (
echo 0!n!
) else (
echo !n!
)
)
pause >nul
#echo off
rem .
rem counter example - with and without padding (up to 260 leading 0s which should be enough for most filenames)
rem .
rem we assume values given are valid
rem additional error checking could be done to make sure they are numbers
rem and to ensure that starting is less than ending
rem and that the number of ending digits is not greater than the number of padding digits
rem .
if "%2"=="" (
echo.
echo usage: %~nx0 [starting number] [ending number] [pad]
echo example: %~nx0 0 19 will output numbers 0 to 19 each on a new line
echo example: %~nx0 3 12 8 will output numbers 3 to 12 each on a new line padded to 8 digits
echo.
goto end
)
rem .
setlocal enabledelayedexpansion
if "%3"=="" (
for /l %%x in (%1, 1, %2) do (
echo.%%x
)
) else (
set "mynum="
for /l %%x in (%1, 1, %2) do (
call set "mynum=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000%%x"
call set "mynum=%%mynum:~-%3%%"
call echo.%%mynum%%
)
)
:end
It is much easier:
set build_b=00%your_value%
set padded_value=%build_b:~-2%

Generating a random number from 0-9 in a Windows batch file

I'm trying to write a Windows batch file that will generate a random number from 0 to 9 and then load a different map list in our game server based on the random number.
I have tried to modify a similar file that was on this forum for generating random characters, but when I reduce the maxchars variables to 1 in length.
I sometimes get #echo is off as a response and sometimes get a number.
Here's what I have:
#echo off & setlocal EnableDelayedExpansion
REM Random.bat
REM
REM Change these values to whatever you want, or change the code to take them
REM as command-line arguments. You must set CHARS_LEN to the string length
REM of the string in the CHARS variable.
REM
REM This script generates a string of these characters at least
REM MIN_CHARS_IN_LINE chars long and at most MAX_CHARS_IN_LINE chars long.
SET CHARS=0123456789
SET /A CHARS_LEN=10 + 10 + 10
SET /A MIN_CHARS_IN_LINE=1
SET /A MAX_CHARS_IN_LINE=2
REM Pick a random line length and output a random character until we reach that
REM length.
call:rand %MIN_CHARS_IN_LINE% %MAX_CHARS_IN_LINE%
SET /A LINE_LENGTH=%RAND_NUM%
SET LINE=
for /L %%a in (1 1 %LINE_LENGTH%) do (
call:rand 1 %CHARS_LEN%
SET /A CHAR_INDEX=!RAND_NUM! - 1
CALL SET EXTRACTED_CHAR=%%CHARS:~!CHAR_INDEX!,1%%
SET LINE=!LINE!!EXTRACTED_CHAR!
)
echo !LINE!
goto:EOF
REM The script ends at the above goto:EOF. The following are functions.
REM rand()
REM Input: %1 is min, %2 is max.
REM Output: RAND_NUM is set to a random number from min through max.
:rand
SET /A RAND_NUM=%RANDOM% * (%2 - %1 + 1) / 32768 + %1
goto:EOF
:eof
Once I can get this reliably choosing characters I just need to add a selection process to the end which will call the server with a different command line for each map list.
The LastStar007 method works great for values between 0 and 9. For a more generic solution that returns a pseudo random number between 0 and n, simply use SET /A to get %random% modulo (n+1).
For example, to get a random number between 0 and 9 on the command line, use
set /a "rand=%random% % 10"
If used in a batch file then the modulo operator must be doubled
set /a "rand=%random% %% 10"
Use the random string in SET and then lop off everything but the last character.
SET RAND=%RANDOM:~-1%
If your problem still persists, try the following. It generates a number between a and b. Tweak it to your needs:
#echo off
color 02
echo enter value of A
set /p a=
echo.
echo enter value of B
set /p b=
:main
set no=%random%
if %no% GEQ %a% goto sub
if not %no% GEQ %a% goto main
:sub
if %no% LEQ %b% goto end
if not %no% LEQ %b% goto main
:end
echo %no%
goto main

Resources