I want to make a batch file that prints out to a text file a list of combination of numbers and letters.
Using {0, ..., 9, a, ..., z, A, ..., Z} as my character pool, I have 62 unique characters.
The word length starts as 1 and increases up to a predetermined value.
The script starts at length = 1 and prints out 0 to Z.
Then it proceeds to length = 2 and prints out 00 to ZZ, and so on...
Here is an iterative solution that is much faster.
No need for CALL.
Each permutation is only generated once.
I was able to generate up to length 4 with over 15 million permutations in less than 5 minutes.
#echo off
setlocal enableDelayedExpansion
set chars=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
set maxPos=61
del output.txt 2>nul
>prior.txt echo(""
for /l %%I in (1 1 %1) do (
>new.txt (
for /f %%A in (prior.txt) do for /l %%N in (0 1 %maxPos%) do echo(%%~A!chars:~%%N,1!
)
type new.txt>>output.txt
move /y new.txt prior.txt >nul
)
del prior.txt
Perhaps this is what you want?
TEST.BAT
#echo off
set charPool=_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
set charLen=62
(for /L %%a in (1,1,%1) do (
set permutation=
call :makePermutation %%a
)) > textfile.txt
goto :EOF
:makePermutation level
setlocal EnableDelayedExpansion
set lastPermutation=%permutation%
for /L %%i in (1,1,%charLen%) do (
set permutation=!lastPermutation!!charPool:~%%i,1!
if %1 gtr 1 (
set /A newLevel=%1-1
call :makePermutation !newLevel!
) else (
echo(!permutation!
)
)
exit /B
The batch file must be started with a number as parameter which is the unit length.
For example on using TEST.BAT 1 the text file textfile.txt contains 62 lines.
Note that TEST.BAT 2 generates 3906 combinations (strictly speaking, permutations in statistical sense) from 0 to ZZ, and TEST.BAT 3 generates 242234 combinations from 0 to ZZZ!
Calculation example for estimating the number of strings in text file (size of file):
Running TEST.BAT with 5 as parameter produces
62 ^ 5 + 62 ^ 4 + 62 ^ 3 + 62 ^ 2 + 62 ^ 1 = 931.151.402
strings in text file.
Related
is there a way to make a If query for specifig
number sections in Batch?
Something Like this:
IF "Var1"=="1-10" (
do something
)
the 1-10 should stand for 1,2,3,4,5,6,7,8,9,10.
I want to make 10 queries (1-10,11-20,21-30, ... , 91-100)
Is that possible?
You can do this:
#echo off
set Var1=1
for /l %%i in (0,1,10) do if %%i==%Var1% echo 10 or below
for /l %%i in (11,1,20) do if %%i==%Var1% echo 11 - 20
for /l %%i in (21,1,30) do if %%i==%Var1% echo 21 - 30
for /l %%i in (31,1,40) do if %%i==%Var1% echo 31 - 40
for /l %%i in (41,1,50) do if %%i==%Var1% echo 41 - 50
for /l %%i in (51,1,60) do if %%i==%Var1% echo 51 - 60
for /l %%i in (61,1,70) do if %%i==%Var1% echo 61 - 70
for /l %%i in (71,1,80) do if %%i==%Var1% echo 71 - 80
for /l %%i in (81,1,90) do if %%i==%Var1% echo 81 - 90
for /l %%i in (91,1,100) do if %%i==%Var1% echo 91 - 100
Here you can change set Var1=1 to any other number and it will correspond. You can replace echo N - N with your commands.
Also, set Var1=1 can be removed from the above example if used with your code as I simply set it to demonstrate the behaviour. Here is an extract from the help when running for /? so you understand the numeric behaviour.
FOR /L %variable IN (start,step,end) DO command [command-parameters]
The set is a sequence of numbers from start to end, by step amount.
So (1,1,5) would generate the sequence 1 2 3 4 5 and (5,-1,1) would
generate the sequence (5 4 3 2 1)
I have a text file with N number of rows and columns, whereas I need to get particular columns with their values and load it into a new text file using batch script, e.g.:
input.txt
col1|col2|col3.....col71|col72
ew|ds|343.....csdk|gfdf
xc|gh|657.....sdfs|utyy
qw|zx|345.....ffds|xzcz
output.txt
col71|col3
csdk|343
sdfs|657
ffds|345
To split text into tokens by (a) certain delimiter(s), use the for /F loop. However, this can only handle up to 31 tokens, so you cannot simply state tokens=71, but you can nest multiple loops:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
> "output.txt" (
rem // Split off the first 31 tokens, pass the rest to the next loop:
for /F "usebackq delims=| eol=| tokens=3,31*" %%A in ("input.txt") do (
rem // Split off the next 31 tokens, pass the rest to the next loop:
for /F "delims=| eol=| tokens=31*" %%D in ("%%C") do (
rem /* Extract the proper token from the remaining ones (remember
rem that 31 + 31 = 62 tokens have been split off before): */
for /F "delims=| eol=| tokens=9" %%F in ("%%E") do (
echo(%%F^|%%A
)
)
)
)
endlocal
If there may be empty columns, the above approach fails, because for /F treats consecutive delimiters as one. To overcome this, you could do the following:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
> "output.txt" (
rem // Read complete lines:
for /F usebackq^ delims^=^ eol^= %%L in ("input.txt") do (
rem // Store current line string in interim variable:
set "LINE=%%L"
setlocal EnableDelayedExpansion
rem /* Split off the first 31 tokens, pass the rest to the next loop;
rem to avoid consecutive delimiters `|`, replace every single one by
rem :`"|"`, so `||` becomes `"|""|"`; then enclose the entire result
rem within `""`, thus achieving individual tokens enclosed within `""`: */
for /F "delims=| tokens=3,31*" %%A in (^""!LINE:|="^|"!"^") do (
endlocal
rem // Split off the next 31 tokens, pass the rest to the next loop:
for /F "delims=| tokens=31*" %%D in ("%%C") do (
rem /* Extract the proper token from the remaining ones (remember
rem that 31 + 31 = 62 tokens have been split off before): */
for /F "delims=| tokens=9" %%F in ("%%E") do (
rem // Remove the previously added surrounding `""` by `~`:
echo(%%~F^|%%~A
)
)
setlocal EnableDelayedExpansion
)
endlocal
)
)
endlocal
This approach will still fail if there are already quoted field values that contain | on their own.
Linux
You could use awk -F "|" '{ print $70 "|" $2 }' input.txt > output.txt.
Usually one would probably execute cut -d"|" -f2,70 input.txt > output.txt, the only problem is that cut (as far as I know) doesn't support reordering columns.
Powershell
On Windows' powershell (also available for Linux) you can use the following snippet:
Get-Content 'input.txt' | ForEach-Object {
$array = $_.split("|")
$array[70] + '|' + $array[2]
} | Out-File 'output.txt'
The following Batch file is a general-purpose program that use a series of nested FOR /F commands that allows access to up to 177 tokens, but in a very simple way:
#echo off
setlocal EnableDelayedExpansion
rem Method to use up to 177 tokens in a FOR /F command in a simple way
rem Antonio Perez Ayala
rem Create an example file with lines with 180 tokens each
(for %%a in (A B C) do (
set "line="
for /L %%i in (1,1,180) do set "line=!line! %%a%%i"
echo !line!
)) > test.txt
set "line="
rem Load the string of tokens characters from FOR-FcharsCP850.txt file
chcp 850 > NUL
if exist FOR-FcharsCP850.txt goto readChars
echo Creating FOR-F characters file, please wait...
set "options=/d compress=off /d reserveperdatablocksize=26"
type nul > t.tmp
> FOR-FcharsCP850.txt (
set /P "=0" < NUL
rem Create 87 characters in 38..124 range for 3 FOR's with "tokens=1-28*"
set "i=0"
for /L %%i in (38,1,124) do (
set /A i+=1, mod=i%%29
if !mod! neq 0 (
call :genchr %%i
type %%i.chr
del %%i.chr
)
)
rem Create 95 characters for 3 FOR's with "tokens=1-31*"
rem This is the tokens sequence used when code page = 850
set "i=0"
for %%i in (173 189 156 207 190 221 245 249 184 166 174 170 240 169 238 248
241 253 252 239 230 244 250 247 251 167 175 172 171 243 168 183
181 182 199 142 143 146 128 212 144 210 211 222 214 215 216 209
165 227 224 226 229 153 158 157 235 233 234 154 237 232 225 133
160 131 198 132 134 145 135 138 130 136 137 141 161 140 139 208
164 149 162 147 228 148 246 155 151 163 150 129 236 231 152 ) do (
set /A i+=1, mod=i%%32
if !mod! neq 0 (
call :genchr %%i
type %%i.chr
del %%i.chr
)
))
del t.tmp temp.tmp
set "options="
:readChars
set /P "char=" < FOR-FcharsCP850.txt
set "lastToken=177"
cls
echo Enter tokens definition string in the same way of FOR /F "tokens=x,y,m-n" one
echo/
echo You may define a tokens range in descending order: "tokens=10-6" = 10 9 8 7 6
echo or add an increment different than 1: "tokens=10-35+5" = 10 15 20 25 30 35
echo Combine them: "tokens=10,28-32,170-161-3" = 10 28 29 30 31 32 170 167 164 161
echo/
echo The maximum token number is 177
:nextSet
echo/
set /P "tokens=tokens="
if errorlevel 1 goto :EOF
rem Expand the given tokens string into a series of individual FOR tokens values
set "tokensValues="
for %%t in (%tokens%) do (
for /F "tokens=1-3 delims=-+" %%i in ("%%t") do (
if "%%j" equ "" (
if %%i leq %lastToken% set "tokensValues=!tokensValues! %%!char:~%%i,1!"
) else (
if "%%k" equ "" (set "k=1") else set "k=%%k"
if %%i gtr %%j set "k=-!k!"
for /L %%n in (%%i,!k!,%%j) do if %%n leq %lastToken% set "tokensValues=!tokensValues! %%!char:~%%n,1!"
)
)
)
rem First three FOR's use as tokens the ASCII chars in 38..124 (&..|) range: 28*3 = 84 tokens + 3 tokens for next FOR
rem Next three FOR's use as tokens Extended chars: 31*3 = 93 tokens + 2 tokens for next FOR
rem based on the tokens sequence used when code page = 850
rem Total: 177 tokens
for /F "eol= tokens=1-28*" %%^& in (test.txt) do ^
for /F "eol= tokens=1-28*" %%C in ("%%B") do ^
for /F "eol= tokens=1-28*" %%` in ("%%_") do ^
for /F "eol= tokens=1-31*" %% in ("%%|") do ^
for /F "eol= tokens=1-31*" %%µ in ("%%·") do ^
for /F "eol= tokens=1-31" %% in ("%%…") do (
call :getTokens result=
rem Process here the "result" string:
echo !result!
)
goto nextSet
:getTokens result=
for %%# in (-) do set "%1=%tokensValues%"
exit /B
REM This code creates one single byte. Parameter: int
REM Teamwork of carlos, penpen, aGerman, dbenham
REM Tested under Win2000, XP, Win7, Win8
:genchr
if %~1 neq 26 (
makecab %options% /d reserveperfoldersize=%~1 t.tmp %~1.chr > nul
type %~1.chr | ( (for /l %%N in (1,1,38) do pause)>nul & findstr "^" > temp.tmp )
>nul copy /y temp.tmp /a %~1.chr /b
) else (
copy /y nul + nul /a 26.chr /a >nul
)
goto :eof
IMPORTANT: The series of six nested FOR /F commands use the following ASCII characters in the replaceable parameter and the character between quotes:
for /F "eol= tokens=1-28*" %%^& in (test.txt) do ^ %%^38
for /F "eol= tokens=1-28*" %%C in ("%%B") do ^ %%67 in ("66")
for /F "eol= tokens=1-28*" %%` in ("%%_") do ^ %%96 in ("95")
for /F "eol= tokens=1-31*" %% in ("%%|") do ^ %%173 in ("124")
for /F "eol= tokens=1-31*" %%µ in ("%%·") do ^ %%181 in ("183")
for /F "eol= tokens=1-31" %% in ("%%…") do ( %%160 in ("133")
However, it seems that some web browser don't correctly copy-paste some extended characters. If the program don't works correctly, you should check that these characters were correctly copied and fix they if necessary. You may try to copy the lines above (in pink background) and test if they were correctly copied...
Output example:
Enter tokens definition string in the same way of FOR /F "tokens=x,y,m-n" one
You may define a tokens range in descending order: "tokens=10-6" = 10 9 8 7 6
or add an increment different than 1: "tokens=10-35+5" = 10 15 20 25 30 35
Combine them: "tokens=10,28-32,170-161-3" = 10 28 29 30 31 32 170 167 164 161
The maximum token number is 177
tokens=10-6
A10 A9 A8 A7 A6
B10 B9 B8 B7 B6
C10 C9 C8 C7 C6
tokens=10-35+5
A10 A15 A20 A25 A30 A35
B10 B15 B20 B25 B30 B35
C10 C15 C20 C25 C30 C35
tokens=10,28-32,170-161-3
A10 A28 A29 A30 A31 A32 A170 A167 A164 A161
B10 B28 B29 B30 B31 B32 B170 B167 B164 B161
C10 C28 C29 C30 C31 C32 C170 C167 C164 C161
tokens=71,3
A71 A3
B71 B3
C71 C3
If your application requires less than 177 tokens, you may modify this program and eliminate the code sections of the not required tokens; that is, with 2 FOR's you may access up to 56 tokens, with 3 up to 84, with 4 up to 115, and with 5 up to 146.
You may review a detailed explanation of this method here; you may also download (a previous version of) this program in a .zip file from this post that would allow to fix the problem of the extended characters in the six FOR /F commands in a simple way...
I am new to Batch and I would like to know if I can find out all combinations of numbers in order.
In this case I have 49 Numbers from 1 - 49 , and I have to pick 6 Numbers to be the results.
For example:
1 2 3 4 5 6
1 2 3 4 5 7
...
1 2 3 4 5 49
1 2 3 4 6 7
1 2 3 4 6 8
etc...
This is my old code:
#echo off > NEWFILE & setLocal EnableDelayedExpansion
set a=44
set b=45
set c=46
set d=47
set e=48
set f=49
for /L %%a in (1 1 !a!) do (
for /L %%b in (2 1 !b!) do (
for /L %%c in (3 1 !c!) do (
for /L %%d in (4 1 !d!) do (
for /L %%e in (5 1 !e!) do (
for /L %%f in (6 1 !f!) do (
echo.%%a %%b %%c %%d %%e %%f
))))))) >> NEWFILE
goto :EOF
However it returns:
1 2 3 4 5 6
1 2 3 4 5 7
...
1 2 3 4 5 49
1 2 3 4 6 6
Two 6's appeared.
I don't seem to be able to fix it, please help, thanks very much!
When you post a question you should post your efforts to solve it, describe the method used and the problems you had; otherwise you may get similar answers with no explanations at all, like this one:
EDIT: As users dbenham and aschipfl indicated, my original code have a small bug: the set /A i=M-1 line should be placed after the :nextSet label. This is the right code:
#echo off
setlocal EnableDelayedExpansion
set "N=%1"
set "M=%2"
set "line="
for /L %%i in (1,1,%M%) do (
set "C[%%i]=%%i"
set "line=!line! ^!C[%%i]^!"
)
:nextSet
set /A i=M-1
for /L %%j in (!C[%M%]!,1,%N%) do (
set "C[%M%]=%%j"
echo %line%
)
:nextPos
set "C=!C[%i%]!"
if %C% equ %N% (
set /A i-=1
if !i! equ 0 goto :EOF
goto nextPos
)
for /L %%i in (%i%,1,%M%) do (
set /A C+=1,C[%%i]=C
)
if !C[%M%]! gtr %N% goto nextPos
goto nextSet
Obviously, the corrected code generate a much larger number of results and this version is particularly slow... :(
The new version below use the exact same code of dbenham's solution; its only advantage is that you may change the parameters used to generate the result in a very easy way:
#echo off
setlocal EnableDelayedExpansion
set "N=%1"
set "M=%2"
set /A j=N-M, prev=0
set "for=" & set "line=" & set "endfor="
for /L %%i in (1,1,%M%) do (
set /A j+=1
set "for=!for! set /A start=!prev!+1 & for /L %%%%i in (^!start^!,1,!j!) do ("
set "line=!line! %%%%i"
set "endfor=!endfor!)"
set "prev=%%%%i"
)
REM ECHO !FOR! echo !LINE! %ENDFOR%
%for% echo %line% %endfor%
Output example:
C:\> test.bat 6 4
1 2 3 4
1 2 3 5
1 2 3 6
1 2 4 5
1 2 4 6
1 2 5 6
1 3 4 5
1 3 4 6
1 3 5 6
1 4 5 6
2 3 4 5
2 3 4 6
2 3 5 6
2 4 5 6
3 4 5 6
To get your results, use: test.bat 49 6
2ND EDIT: Faster method added
When the problem to solve is the excessive time a process takes, an obvious alternative is to use a faster programming language. The solution below use JScript, that is somewhat similar to Batch file programming:
#if (#CodeSection == #Batch) #then
#echo off
echo Start: %time%
cscript //nologo //E:JScript "%~F0" > result.txt
echo End: %time%
goto :EOF
#end
// JScript code section
for ( var A=1; A <= 44; ++A ) {
for ( var B=A+1; B <= 45; ++B ) {
for ( var C=B+1; C <= 46; ++C ) {
for ( var D=C+1; D <= 47; ++D ) {
for ( var E=D+1; E <= 48; ++E ) {
for ( var F=E+1; F <= 49; ++F ) {
WScript.Echo(A,B,C,D,E,F);
}
}
}
}
}
}
This is a Batch-JScript hybrid script; save it with .BAT extension. This program took a little less than 9 minutes in my cheap-and-slow lap-top computer to generate a 239 MB file with 13983816 lines.
The problem is compute intensive, given that there are 13,983,816 unique permutations. (See https://en.wikipedia.org/wiki/Lottery_mathematics#Calculation_explained_in_choosing_6_from_49.)
The Rojo answer should work, but the GOTO and repetitive FOR /F parsing and IF logic will slow things down considerably.
The code is much faster if you use nested FOR /L loops.
#echo off
setlocal enableDelayedExpansion
for /l %%A in (1 1 44) do (
set /a start=%%A+1
for /l %%B in (!start! 1 45) do (
set /a start=%%B+1
for /l %%C in (!start! 1 46) do (
set /a start=%%C+1
for /l %%D in (!start! 1 47) do (
set /a start=%%D+1
for /l %%E in (!start! 1 48) do (
set /a start=%%E+1
for /l %%F in (!start! 1 49) do (
echo %%A %%B %%C %%D %%E %%F
)
)
)
)
)
)
This will still be unbearably slow to let this script print the results to the screen. I estimate it will take 1.25 hours on my machine. Redirecting the output to a file is about 5 times faster, around 15 minutes.
In the future, please show some code demonstrating that you've attempted to solve the problem yourself, showing where you got stuck, where the output is not as expected, etc. Questions resembling "Here are my requirements. Code this for me" generally aren't well-received around here. How you got an upvote without showing any code is beyond me, but c'est la vie.
In this instance, I found the problem interesting, so I thought I'd go ahead and get you started. Challenge: accepted. Here's one way to do it.
#echo off
setlocal enabledelayedexpansion
set "series=1 2 3 4 5 6"
:loop
echo %series%
if "%series%"=="44 45 46 47 48 49" goto :EOF
for /f "tokens=1-6" %%a in ("%series%") do (
set /a i1=%%a, i2=%%b, i3=%%c, i4=%%d, i5=%%e, i6=%%f+1
if !i6! gtr 49 set /a i5+=1, i6=i5+1
if !i5! gtr 48 set /a i4+=1, i5=i4+1, i6=i5+1
if !i4! gtr 47 set /a i3+=1, i4=i3+1, i5=i4+1, i6=i5+1
if !i3! gtr 46 set /a i2+=1, i3=i2+1, i4=i3+1, i5=i4+1, i6=i5+1
if !i2! gtr 45 set /a i1+=1, i2=i1+1, i3=i2+1, i4=i3+1, i5=i4+1, i6=i5+1
set "series=!i1! !i2! !i3! !i4! !i5! !i6!"
)
goto loop
Here's another solution that should be more efficient.
#echo off
setlocal enabledelayedexpansion
set "series=1 2 3 4 5 6"
set total=0
for /L %%a in (1,1,44) do (
set /a i2 = %%a + 1
for /L %%b in (!i2!, 1, 45) do (
set /a i3 = %%b + 1
for /L %%c in (!i3!, 1, 46) do (
set /a i4 = %%c + 1
for /L %%d in (!i4!, 1, 47) do (
set /a i5 = %%d + 1
for /L %%e in (!i5!, 1, 48) do (
set /a i6 = %%e + 1
for /L %%f in (!i6!, 1, 49) do (
rem // Uncomment this echo to watch the progress (severely decreases efficiency)
rem echo %%a %%b %%c %%d %%e %%f
set /a total += 1
)
)
)
)
)
echo Total so far: !total!
)
rem // Should have gone through 13983816 iterations
In a Windows cmd script (aka bat script), I have a FOR /L loop from 1 to 8, where I need to do a bit shift and somehow format a variable as a hexadecimal number (which if you ask, is a single CPU identifier bit to feed into /AFFINITY).
I can't figure out how to do the last step. This is my loop.cmd file:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /L %%i IN (1,1,8) DO (
SET /A "J=1<<%%i"
ECHO %%i and !J!
)
which does everything but format a hex number:
1 and 2
2 and 4
3 and 8
4 and 16
5 and 32
6 and 64
7 and 128
8 and 256
expected output is:
1 and 2
2 and 4
3 and 8
4 and 10
5 and 20
6 and 40
7 and 80
8 and 100
How do you format a hexadecimal number?
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /L %%i IN (1,1,8) DO (
SET /A "J=1<<%%i"
CALL :DECTOHEX J
ECHO %%i and !J!
)
GOTO :EOF
:DECTOHEX VAR
SET "DEC=!%1!"
SET "HEX="
:NEXT
SET /A DIGIT=DEC%%16, DEC/=16
SET "HEX=%DIGIT%%HEX%"
IF %DEC% NEQ 0 GOTO NEXT
SET "%1=%HEX%"
EXIT /B
EDIT: Reply to the comment
Previous solution works correctly when the shifted value have just one bit on, as stated in the question. If the shifted value may have several bits on then a more general decimal-to-hexadecimal conversion is required, like the one below:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
REM DEFINE THE HEXA DIGITS
SET "HEXA=0123456789ABCDEF"
FOR /L %%i IN (1,1,8) DO (
SET /A "J=3<<%%i"
CALL :DECTOHEX J
ECHO %%i and !J!
)
GOTO :EOF
:DECTOHEX VAR
SET "DEC=!%1!"
SET "HEX="
:NEXT
SET /A DIGIT=DEC%%16, DEC/=16
SET "HEX=!HEXA:~%DIGIT%,1!%HEX%"
IF %DEC% NEQ 0 GOTO NEXT
SET "%1=%HEX%"
EXIT /B
#echo off
setlocal enabledelayedexpansion
set x=2
set n=1
set /a result=n
for /l %%a in (1,1,10) do (
set /a result*=x
if "!result:~0,1!"=="1" set result=!result:16=10!
echo %%a and !result!
)
output:
1 and 2
2 and 4
3 and 8
4 and 10
5 and 20
6 and 40
7 and 80
8 and 100
9 and 200
10 and 400
I have 2 text files; A.txt and B.txt and I want to merge them to make C.txt using a batch script.
However (here's the tricky part) I wish to do it so each line from A.txt is appended with a space then the first line from B.txt then a new line with the first line from A and the second from B and so on until the end of B is reached and then we start with the second line of A.
I know I haven't worded this well so here's an example:
A.txt;
1
2
3
4
5
B.txt;
Z
Y
X
W
V
T
R
So C.txt would have;
1 Z
1 Y
1 X
1 W
1 V
1 T
1 R
2 Z
2 Y
etc.
#echo off
for /f "delims=" %%a in (a.txt) do (
for /f "delims=" %%b in (b.txt) do (
>>c.txt echo %%a %%b
)
)