Solved Randomly grab multiple files and move it to the folder

March 11, 2019 at 20:16:47
Specs: Windows 7, i7/8gb
I have a library of 1000 questions placed in a folder. I want to create a test bank with each exam taken randomly from the library of questions above. Each topic consists of 10 questions that are taken randomly and copied into 1 exam paper. Does the batch script solve this problem? This is a script to copy a random file I searched for
cd e:\finaly01
set n = 0
for %% f in (*) do (
set / A n + = 1
set "file [! n!] = %% f"
)
for / L %% i in (1.1,% time: ~ -1%) by set "dummy =! random!"
set / A "rand = (n *% random%) / 32768 + 1"
copy "! file [% rand%]!" "e:\example"
Because I want to create 10 random exam questions, I added:
for / l% x in (1, 1, 10) do
cd e:\finaly01
set n = 0
for %% f in (*) do (
set / A n + = 1
set "file [! n!] = %% f"
)
for / L %% i in (1.1,% time: ~ -1%) by set "dummy =! random!"
set / A "rand = (n *% random%) / 32768 + 1"
copy "! file [% rand%]!" "e:\example"
but it doesn't seem to work.
Can someone help to solve it? thank you very much!

message edited by hoannh


See More: Randomly grab multiple files and move it to the folder

Reply ↓  Report •

#1
March 11, 2019 at 23:31:36
Kind of looks like a lot of overkill-coding. Also to confirm my assumptions:
There are 1000 files, named 1 to 1000? And each file contains a single one-line question or problem-description? Like:
filename         content
1             what's your name?
2             what's your favorite color
3             what's the atomic weight of zirconium at C

:: begin batchscript
@echo off>exam1 & setlocal
del test1 >nul 2>&1
for /l %%a in (1 1 10) do call :xx %%a
echo done:
type exam1
goto :eof

:xx
set /a z=%random% %% 1000+1
rem this next is to prevent duplicate questions - 'test1' is only used to check for that.
find "%z%" test1&&goto :xx
>>test1 echo %z%
type %z%>>exam1

only minimally tested....

message edited by nbrane


Reply ↓  Report •

#2
March 11, 2019 at 23:56:12
Sorry for the non-specific question. Each question is saved in a docx file named question001.docx.
I want to create 10 filder 1,2,3,4,5,6,7,8,9,10 and each of these folders contains question001.docx files, question011.docx, question004.docx, question022.docx, question013. docx, ... get random from the library folder question.

Reply ↓  Report •

#3
March 12, 2019 at 19:59:56
✔ Best Answer
Ok. Best way since the names aren't numeric, build a temp file and use that:
@echo off>library & setlocal
for /f  "tokens=*" %%a in ('dir /b question*.docx^|find /v /n ""') do >>library echo %%a
set c=1
:xx
md %c% 2>nul
set x=1
:x1
set /a k=%random% %% 1000+1
for /f "tokens=1* delims=[]" %%q in ('find "[%k%]"^<library') do copy %%r %c%\%x%.docx
set /a x+=1
if %x% leq 10 goto :x1

set /a c+=1
if %c% lss 11 goto :xx

The only other question I have, do you want a given question to only appear in one test, or doe that not matter? f/e exam1 might have "what's your name", and exam2 might also have "what's your name". Aside from that, this test script seemed to work.
ten directories were created with ten question___.docx files each.

message edited by nbrane


Reply ↓  Report •

Related Solutions

#4
March 13, 2019 at 12:28:32
Well, not really! There is a fatal flaw: the possibility of a question appearing more than once in a given exam. This version tries to fix that problem:
@echo off & setlocal
:: no need to rebuild the library every time.
if exist library goto :already
for /f  "tokens=*" %%a in ('dir /b question*.docx^|find /v /n ""') do >>library echo %%a
:already
set c=1
:xx
md %c% 2>nul
echo %c%
rem pause
set x=1
set b=

:x1
set /a k=%random% %% 1000+1
set k=[%k%]
rem echo %k%
echo %b%|find "%k%">nul&&goto :x1
set b=%b%%k% 
for /f "tokens=1* delims=[]" %%q in ('find "%k%"^<library') do copy /y %%r %c%\%x%>nul
set /a x+=1
if %x% leq 10 goto :x1

set /a c+=1
if %c% lss 11 goto :xx


if the current random candidate is found in var. %b%, then it loops to generate a new random. Without this fix, the "exams" could be fatally flawed. I hope the submitting party comes back and gets the corrected script.

message edited by nbrane


Reply ↓  Report •

#5
March 17, 2019 at 20:41:13
So, getting a random sample of things is something that comes up often enough that I have scripts throughout the years. These days, where PowerShell is effectively my main language, it's fairly simple. Turns out it comes up often enough for enough people that it's just built in:
md someDir
dir *.docx |
 Get-Random -c 10 |
 copy -dest someDir

But I also have batch scripts to do the same. I ultimately ended up with copying one file at random until I got the desired amount. A duplicate pick will overwrite the original pick, meaning the loop just runs one extra time. Included is a check of the candidate files and the desired random sample size. If the desired sample size is equal to or greater than the pool size, it just copies all files instead. To avoid an endless loop.

SET files=*.docx
CALL :fileCount srcTotal %files%
MD someDir

IF %srcTotal% LEQ 10 GOTO tooFew
:loop
SET /A rnd=%random% %% %srcTotal% + 1
FOR /F "tokens=1* delims=:" %%a IN ('DIR %files% /B ^| FINDSTR /N "." ^| FIND "%rnd%:"') ^
DO COPY %%b someDir
CALL :fileCount destTotal someDir\%files%
IF NOT %destTotal%==10 GOTO loop
GOTO :EOF

:tooFew
COPY %files% someDir
GOTO :EOF

:fileCount
REM %1 = Return var, %2 = Search path
FOR /F %%a IN ('DIR %2 ^| FIND "File(s)"') DO SET %1=%%a

Unfortunately, I couldn't find an old VBScript for random selection. I'm sure I wrote a few back when I wrote a lot of VBScript, and I'd hate for this list to be incomplete without Windows' third and most encompassing scripting language, so I threw something together. Since I felt like going back to the 1980's, I decided to use a single array to hold both the pool list and the sample list while listening to synthwave.

You see, once I pick a file, I remove it from the pool. Then I take the last element off the list, and shove it into the resulting hole. Decrease the upper bounds of the pool to hide the hole at the end of the list, and I'm ready to pick another from the pool. I just have to do something with the selected file. Might as well shove it into the "hidden" hole at the end! There's also a pool size check in here as well, otherwise we could underflow the array. The things we did when arrays were fixed and memory was expensive. These days you should probably just use two ArrayLists or dictionaries for both.

Const ext = "DOCX" 'Must be uppercase
Const rndCnt = 10
Randomize

'Get list of relevant files
Set fso = CreateObject("Scripting.FileSystemObject")
For Each f In fso.GetFolder(".").Files
	If UCase(fso.GetExtensionName(f)) = ext Then _
		srcCnt = srcCnt + 1
Next 'f
ReDim files(srcCnt) : i = 1
For Each f In fso.GetFolder(".").Files
	If UCase(fso.GetExtensionName(f)) = ext Then
		Set files(i) = f : i = i + 1
	End If
Next 'f

If srcCnt >= rndCnt Then
	'Get random selection
	endElement = srcCnt
	For i = 1 To rndCnt
		num = Int(Rnd * endElement + 1)
		Set tmp = files(num)
		Set files(num) = files(endElement)
		Set files(endElement) = tmp
		endElement = endElement - 1
	Next 'i
Else
	'Not enough elements to get a random selection, copy everything
	endElement = 0
End If

'Copy random selection to someDir
If Not fso.FolderExists("someDir") Then _
	fso.CreateFolder "someDir"
For i = endElement + 1 To srcCnt
	files(i).Copy "someDir\"
Next 'i


How To Ask Questions The Smart Way

message edited by Razor2.3


Reply ↓  Report •

Ask Question