Randomly choose 15 files and copy them to output folder - windows

Must work on XP/Vista/7. Can use batch, VBS, or whatever else anyone with the mentioned O/Ses can run (except PE).
Need to choose 15 random files, and also similarly named folders (which are in a different location), and copy them to their own folder at the same time.
I've scoured google and websites like robvanderwoude.com, and found a few close examples, but I'm too inexperienced to adapt the examples to what I need without going cross-eyed. I'd appreciate it if anyone could point me in the right direction (most efficient/easiest method to use), or some example possibly with a brief explanation I can also learn from.
Layout description:
30 files:
%~dp0\mod\store\XMLs -> %~dp0\mod\0.1.2\map\data
map01_aaa.xml
map02_bbb.xml
map03_ccc.xml
...
map60_zzz.xml
30 folders:
%~dp0\mod\store\models -> %~dp0\mod\0.1.2\sky\stuff
01_aaa_map
02_bbb_map
03_ccc_map
...
60_zzz_map
The code below is what I'm trying to adopt this to, but it only chooses 15 files/folders in order. Tried using the %random% environment var in an equation for SrcMax, but that just chooses a random amount of files and always starts with the first file.
(old code)
rem #ECHO OFF
setlocal ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS
SET SrcCount=0
SET SrcMax=15
FOR %%F IN (%~dp0\mod\store\XMLs\*.*) DO IF !SrcCount! LSS %SrcMax% (
SET /A SrcCount += 1
ECHO !SrcCount! COPY %%F %~dp0\mod\0.1.2\map\data\
COPY %%F %~dp0\mod\0.1.2\map\data\
SET FNAME=%%~nF
ECHO XCOPY /s "%~dp0\mod\store\Models\!FNAME:~3!_map" "%~dp0\mod\0.1.2\sky\stuff\!FNAME:~3!_map\"
XCOPY /s "%~dp0\mod\store\Models\!FNAME:~3!_map" "%~dp0\mod\0.1.2\sky\stuff\!FNAME:~3!_map\"
)

I believe this should work.
#echo off
setlocal enabledelayedexpansion
set XMLs_src=.\mod\store\XMLs
set XMLs_dest=.\mod\0.1.2\map\data
set maps_src=.\mod\store\models
set maps_dest=.\mod\0.1.2\sky\stuff
rmdir /q /s "%XMLs_dest%" 2>NUL
rmdir /q /s "%maps_dest%" 2>NUL
mkdir "%XMLs_dest%"
mkdir "%maps_dest%"
for /L %%X in (1,1,15) do (
call :rnd rn
call :xml !rn!
call :map !rn!
)
copy "%XMLs_dest%\*.*" "%XMLs_src%" >NUL
echo d | xcopy /q /f /e /y "%maps_dest%\*" "%maps_src%" >NUL
echo Done.
goto :EOF
:rnd
set c=0
for /d %%I in (%maps_src%\*) do set /a c+=1 >NUL
set /a %1=%RANDOM% * %c% / 32768 + 1 >NUL
goto :EOF
:xml
set c=0
for /f %%I in ('dir /b /o:n "%XMLs_src%"') do (
set /a c+=1 >NUL
if !c!==%1 (
echo %XMLs_src%\%%I -^> %XMLs_dest%\%%I
move "%XMLs_src%\%%I" "%XMLs_dest%" >NUL
goto :EOF
)
)
goto :EOF
:map
set c=0
for /f %%I in ('dir /b /o:n "%maps_src%"') do (
set /a c+=1 >NUL
if !c!==%1 (
echo %maps_src%\%%I -^> %maps_dest%\%%I
echo d | xcopy /q /f /e /y "%maps_src%\%%I" "%maps_dest%\%%I" >NUL
rmdir /q /s "%maps_src%\%%I"
goto :EOF
)
)
goto :EOF
It basically moves to prevent duplication, then copies from destination back to source to restore what was moved away.

Related

Batch How to avoid a same 2nd FOR loop?

I have created a batch script to backup some subfolders from a path A to a path B (Z:\Folder1\… > U:\Backup\…).
The script lists the subfolders inside path A and increments a number for each of them.
Then, I just have to enter the number(s) of the subfolder(s) I want to backup and xcopy does the rest.
The problem is that sometimes I have thousands of subfolders in path A and only a few to backup (10 or 15).
What I would like is that once I enter the number of these folders, it will go straight to the backup without having to loop all the subfolders inside path A AGAIN (which take time).
Here is my batch :
#echo off
setlocal EnableDelayedExpansion
rem Script for backuping some subfolders from path A to path B
set BackupLocation=U:\Backup
set subfolder_no=1
FOR /F "delims=" %%a IN ('dir /b "Z:\Folder1\*"') DO (
set subfolder=%%a
if defined subfolder (
echo !subfolder_no! !subfolder!
set /a subfolder_no+=1
)
)
set /a subfolder_no=%subfolder_no%-1
set /a index=0
set /a choice=-1
echo.
set /p choice=Enter the number(s) of the subfolder(s) you want to backup:
FOR /F "delims=" %%a IN ('dir /b "Z:\Folder1\*"') DO (
set subfolder=%%a
if defined subfolder (
set /a index+=1
)
FOR %%f IN (%choice%) DO if %%f==!index! (
echo.
echo Backuping subfolder !subfolder!
xcopy "Z:\Folder1\!subfolder!" "%BackupLocation%\!subfolder!\" /e /i /y
)
)
echo.
pause
exit
How can I do this ? Is it possible to get the subfolders' name from their matching number and store them in variables or something ?
Thanks a lot for your help !
Here's an example which only enumerates the directories once.
Please note, whilst it technically answers your question, and performs the task you require of it, this version is designed to work as intended on Windows 10. The specific part you asked about works in other versions too, but the :BackUp labelled section uses a new and undocumented Windows 10 feature of the sort command, to return only unique items from the selection list. Without that, your end user could tehnically provide the same number multiple times, and thus trigger multiple backups of the same directory. As this part of the code is technically outside of the scope of your question, I will leave it up to you to modify the code section yourself, should you be deploying this on older Operating Systems.
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Rem Script for backing up some subdirectories of a Source path to a Backup path.
Set "SourceLocation=Z:\Folder1"
Set "BackupLocation=U:\Backup"
If Not Exist "%Sourcelocation%\." Exit /B
If Not Exist "%Backuplocation%\." Exit /B
:ShowSet
For /F "Delims==" %%G In ('"(Set subdirectory[) 2>NUL"') Do Set "%%G="
Set "index=0"
For /F "EOL=? Delims=" %%G In ('Dir /B /A:D /O:N "%SourceLocation%" 2^>NUL'
) Do (Set /A index += 1
Set "subdirectory=%%G"
SetLocal EnableDelayedExpansion
Echo !index! !subdirectory!
For /F "Tokens=1,*" %%H In ("!index! !subdirectory!") Do (EndLocal
Set "subdirectory[%%H]=%%I"))
If Not Defined subdirectory[1] Exit /B
:Select
Echo(
Echo Please type the number(s) for the subdirectories you want to backup,
Echo(
Echo For multiple selections please separate each with spaces e.g. 1 3 6
Echo For none please type 0 or press [ENTER].
Set "selection=0"
Set /P "selection=>"
Set "selection=%selection:"=%"
Set "selection=%selection:)=%"
If Not Defined selection GoTo Select
If "%selection%" == "0" GoTo :EOF
(Set selection) | "%SystemRoot%\System32\findstr.exe"^
/X /R /C:"selection=[0123456789 ][0123456789 ]*" 1>NUL || GoTo Select
Set "selection=%selection% "
:BackUp
For /F %%G In (
'"(Echo(%selection: =^&Echo(%) | "%SystemRoot%\System32\sort.exe" /Unique"'
) Do If Not Defined subdirectory[%%G] (Echo Selection %%G was not valid) Else (
SetLocal EnableDelayedExpansion
Echo(&Echo Backing up subdirectory %%G !subdirectory[%%G]!
"%SystemRoot%\System32\Robocopy.exe" ^
"%SourceLocation%\!subdirectory[%%G]!" ^
"%BackupLocation%\!subdirectory[%%G]!" /E /NC /NDL /NJH /NJS /NS ^
| %SystemRoot%\System32\find.exe /V " (0x00000005) "
EndLocal)
Pause
In the :BackUp section, I have used Robocopy.exe instead of the deprecated xcopy.exe utility you had used.
If you wish to still use xcopy.exe, replace:
"%SystemRoot%\System32\Robocopy.exe" ^
"%SourceLocation%\!subdirectory[%%G]!" ^
"%BackupLocation%\!subdirectory[%%G]!" /E /NC /NDL /NJH /NJS /NS ^
| %SystemRoot%\System32\find.exe /V " (0x00000005) "
with:
"%SystemRoot%\System32\xcopy.exe" ^
"%SourceLocation%\!subdirectory[%%G]!" ^
"%BackupLocation%\!subdirectory[%%G]!\" /E /I /Y

How use a batch to copy two of the most recent of each type of file from a folder with many types of logs

I found a post where the user wanted to copy the 2 most recent log folders to another location answered by Mofi. I am attempting something similar except I have a folder that includes multiple types of troubleshooting logs. I have attempted to copy only specific logs with mixed success. I am able to copy two of the most recent logs for two of the log types but am having no success with the others. It also seems that using a pause in the batch is not stopping it to see any error messages.
I was successful in copying the localhost log and the server log. However, the diagnostic log does not copy or any other logs in the folder I attempt to move. All files in the folder are .log extensions but and after a certain size roll over to a name.log.date format, but that hasn't seemed to matter for the localhost and server logs. I have tried putting in pauses at the end of each block and even within the blocks but the batch doesn't stop so I do not get a chance to see any errors.
#echo off
mkdir N:\Copy_logs
setlocal EnableExtensions EnableDelayedExpansion
REM -----------------
REM localhostlog
REM -----------------
set FileCount=02
set "SourcePath=D:\applications\server\log"
set "TargetPath=N:\Copy_logs"
set "SourcePath=%SourcePath:/=\%"
set "TargetPath=%TargetPath:/=\%"
if not "%SourcePath:~-1%" == "\" set "SourcePath=%SourcePath%\"
if not "%TargetPath:~-1%" == "\" set "TargetPath=%TargetPath%\"
for /F "eol=| delims=" %%I in ('dir "%SourcePath%/localhost_access_log.*" /A-D /B /O-D 2^>nul') do (
%SystemRoot%\System32\xcopy.exe "%SourcePath%%%I" "%TargetPath%" /C /I /Q /H /R /Y >nul
set /A FileCount-=1
if !FileCount! == 0 goto serverLog
)
REM -----------------
REM serverLog
REM -----------------
:serverLog
set FileCount=02
set "SourcePath=D:\applications\server\log"
set "TargetPath=N:\Copy_logs"
set "SourcePath=%SourcePath:/=\%"
set "TargetPath=%TargetPath:/=\%"
if not "%SourcePath:~-1%" == "\" set "SourcePath=%SourcePath%\"
if not "%TargetPath:~-1%" == "\" set "TargetPath=%TargetPath%\"
for /F "eol=| delims=" %%I in ('dir "%SourcePath%/server.*" /A-D /B /O-D 2^>nul') do (
%SystemRoot%\System32\xcopy.exe "%SourcePath%%%I" "%TargetPath%" /C /I /Q /H /R /Y >nul
set /A FileCount-=1
if !FileCount! == 0 goto diagLog
)
REM -----------------
REM Diagnostic Log
REM -----------------
:diagLog
set FileCount=02
set "SourcePath=D:\applications\server\log"
set "TargetPath=N:\Copy_logs"
set "SourcePath=%SourcePath:/=\%"
set "TargetPath=%TargetPath:/=\%"
if not "%SourcePath:~-1%" == "\" set "SourcePath=%SourcePath%\"
if not "%TargetPath:~-1%" == "\" set "TargetPath=%TargetPath%\"
for /F "eol=| delims=" %%I in ('dir "%SourcePath%/diagnostic.*" /A-D /B /O-D 2^>nul') do (
%SystemRoot%\System32\xcopy.exe "%SourcePath%%%I" "%TargetPath%" /C /I /Q /H /R /Y >nul
set /A FileCount-=1
if !FileCount! == 0 goto :FileCopyDone
)
:FileCopyDone
endlocal
The expected result is the copying of two of the most recent files of each type of log. The first two types of logs copy but any of the remaining logs do not copy.
I would suggest that you use a sub function.
Quick untested example:
#Echo Off
Set "source=D:\applications\server\log"
Set "destination=N:\Copy_logs"
Set "number=2"
PushD "%source%" 2>NUL || GoTo :EOF
For %%A In ("localhost_access_log" "server" "diagnostic") Do If Exist "%%~A.*" Call :Sub %%A
PopD
GoTo :EOF
:Sub
For /F "Tokens=1*Delims=[]" %%A In (
'Dir /B/A-D/O-D/TW "%~1.*" 2^>NUL^|"%__AppDir__%find.exe" /N /V ""') Do (
If %%A GTR %number% Exit /B
"%__AppDir__%xcopy.exe" "%source%\%%B" "%destination%\" /Y)

How do I count all files in all sub folders in a directory and its sub folders but return the name of the sub folder with a count of the files?

Hoping someone can help me out here. I have a folder which holds a number of sub folders - e.g.:
Business
Chemistry
DT
English
Each one of those folders has a number of sub folders.
What I would like to do is to run a batch file which would then output something like this:
Business: 24
Chemistry: 542
DT: 298
English: 101
I have a command line command, which when run in the sub folder, returns the number of files in that folder and sub folders, but this is quite a laborious process to have to run the command 50+ times.
Give a try for this batch file :
#echo off
(
FOR /D %%G in ("*") DO (
PUSHD "%%G"
FOR /F "delims=" %%H in ('dir /a-d /b * ^|find /C /V ""') DO echo %%G : %%H
POPD
)
)>"..\count.txt"
Start "" "..\count.txt"
This should work for you, (and should be relatively quick for larger directories):
#Echo Off
SetLocal EnableDelayedExpansion
(Set selDir=C:\Users\Robert\Subjects)
For /D %%A In ("%selDir%\*") Do Call :Sub "%%A"
Timeout -1 1>Nul
Exit/B
:Sub
Set i=3
For /F "EOL=- Tokens=1-3" %%A In (
'RoboCopy /L /E /NFL /NDL /NJH %1 null *.*') Do (
If %%B==: (Set/A i-=1
If !i! Equ 1 Echo(%~nx1 : %%C))
Just make sure you change the path within the parentheses on line four to suit your parent directory.
Supposing your directories are located in D:\root\, the following counts the immediate sub-directories of each:
#echo off
for /D %%R in ("D:\root\*") do (
set "NAME=%%~nxR"
set /A "COUNT=0"
for /D %%D in ("%%~fR\*") do (
set /A "COUNT+=1"
)
setlocal EnableDelayedExpansion
echo(!NAME!: !COUNT!
endlocal
)

look for file of a certain type one subfolder down, sort by date modified (newest first), and display subfolder name (NOT filename) in sorted order

This is a very specific question, so I've separated it into 3 parts.
Look in all subfolders for a certain file: world.sav, but only 1 level down, i.e.
C:\workingdir\foo\world.sav
C:\workingdir\bar\world.sav
C:\workingdir\baz\world.sav
C:\workingdir\qux\world.sav
is fine, but
C:\workingdir\foo\bar\world.sav
C:\workingdir\world.sav
isn't, etc.
Sort these world.sav files by date modified (newest first).
Display the name of the subfolder each world.sav file is in, in the previously sorted order. i.e. if the above list was date-sorted into
C:\workingdir\qux\world.sav (newest)
C:\workingdir\bar\world.sav
C:\workingdir\foo\world.sav
C:\workingdir\baz\world.sav (oldest)
then the output would be
qux
bar
foo
baz
I've tried numerous methods involving DIR, FORFILES and manipulation of variables, but the 2 main problems I've come across so far are
Both commands mentioned above will arrange by subfolder, then by date, and I can't find a way to avoid it.
Date formatting. It appears to be different for every localization of Windows, and I really want this to be locale-independent.
Also, it must support spaces in folder names.
EDITED version with a wait message:
#echo off
echo Please wait...
cd /d "c:\workingdir"
(for /d %%a in (*) do #robocopy "%%a" "%%a" world.sav /L /nocopy /is /njh /njs /ndl /nc /ns /ts)|(
#cls
for /f "tokens=2*" %%b in ('sort /r') do #for %%z in ("%%~dpc\.") do #echo %%~nz
)
cd /d "%~dp0"
echo.
pause
original version below
This works here:
robocopy is used on each folder in the directory to generate a list (UTCdate time drv:\path\world.sav format) and that is sorted to get most recent at the top of the list, which the second for parses to extract the drv:\path\folder\world.sav and the last for loop prints out just the folder name.
#echo off
cd /d "c:\workingdir"
(for /d %%a in (*) do #robocopy "%%a" "%%a" world.sav /L /nocopy /is /njh /njs /ndl /nc /ns /ts)|for /f "tokens=2*" %%b in ('sort /r') do #for %%z in ("%%~dpc\.") do #echo %%~nz
pause
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET /a count=0
:: remove variables starting $
FOR /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="
FOR /f "delims=" %%a IN (
'dir /b /ad "%sourcedir%\*" '
) DO IF exist "%sourcedir%\%%a\world.sav" CALL :set$ "%%a"
IF %count%==0 ECHO No files found&GOTO :EOF
IF %count%==1 ECHO %$1%&GOTO :EOF
:sortol
SET /a start=1
SET /a swap=0
:sortil
CALL :compdt
IF %start% neq %count% GOTO sortil
IF %swap% neq 0 GOTO sortol
FOR /l %%a IN (1,1,%count%) DO CALL ECHO %%$%%a%%
GOTO :EOF
:set$
SET /a count+=1
SET "$%count%=%~1"
GOTO :eof
:compdt
CALL SET f1=%%$%start%%%
SET /a START +=1
CALL SET f2=%%$%start%%%
FOR /f %%a IN (
'echo f^|xcopy /L /y /d "%sourcedir%\%f1%\world.sav" "%sourcedir%\%f2%\world.sav"'
) DO SET copies=%%a
IF %copies%==0 GOTO :EOF
SET /a swap +=1
SET /a START -=1
SET "$%start%=%f2%"
SET /a START +=1
SET "$%start%=%f1%"
GOTO :eof
This worked for me. You'd need to set sourcedir to suit your purpose.
Essentially, it builds a list of the next-level-down directory names which contain the target filename in variables $1...$whatever, then sorts the names using a simple bubble-sort which relies on the characteristics of xcopy. XCOPY's /L switch simply lists the files that would be copied; /y tells it to 'go ahead (and not copy)' and /d says 'only later files.ECHOingftellsxcopythat the desired "copy" is a file-copy, so it responds1 file(s) copiedor0...` as its last line, depending on whether the source file was later than the destination.
To reverse the order, simply change the if %copies%==0 to if %copies%==1
An idea :
#echo off
Setlocal EnableDelayedExpansion
set $WorkDir="C:\eh\guh\blugh\fneh\mork\workingdir"
for /f "tokens=*" %%a in ('dir /s/b/a-d %$WorkDir%') do (
for /f "tokens=9 delims=\" %%b in ('echo %%a') do (
if /i "%%b"=="world.sav" call:next "%%~pa" %%~ta
)
)
::Resultat
for /f "tokens=2 delims==" %%r in ('set #') do echo %%r
exit/b
:next
for /f "tokens=7 delims=\" %%c in ('echo %~1') do set $Path=%%c
for /f "tokens=1-3 delims=/" %%c in ('echo %~2') do set #%%e%%d%%c=!$path!

Using batch script to automatically copy latest file to folder

I would like to call for help on my attempt to create a script which will automatically check last modified file and transfer it to destination.
What I have is this:
#Echo Off
Set _Delay=1
for /f "tokens=1,2,3 delims==" %%a in (config.ini) do (
if %%a==source set _Monitor=%%b
if %%a==destination set _Dest=%%b
if %%a==timer set _Timer=%%b
)
Set _Base=%temp%\BaselineState.dir
Set _Chck=%temp%\ChkState.dir
Set _OS=6
Ver|Findstr /I /C:"Version 5">Nul
If %Errorlevel%==0 Set _OS=5 & Set /A _Delay=_Delay*1000
Goto :_Xfer
:_StartMon
#echo off
SET R1=Start /w Sync1.vbs
Call :_SetBaseline "%_Base%" "%_Monitor%"
:_MonLoop
If %_OS%==5 (Ping 1.0.0.0 -n 1 -w %_Delay%>Nul) Else Timeout %_Delay%>Nul
Call :_SetBaseline "%_Chck%" "%_Monitor%"
FC /A /L "%_Base%" "%_Chck%">Nul
If %ErrorLevel%==0 Goto _MonLoop
::
for /l %%a in (%_Timer%,-1,0) do (
#Echo::::::::::::::::::::::::::::::::::::::::::::::
#Echo:: SEC Auto Transfer ::
#Echo:: Aizal 16 June 2012 ::
#Echo::::::::::::::::::::::::::::::::::::::::::::::
#Echo This script will scan SEC Data Folder for
#Echo new files every 30 minutes and will copy
#Echo it to the Server. Make sure you set correct
#Echo folder before using this script.
ECHO.
ECHO Currently recording:
FOR /F %%a in ('xcopy "%_Monitor%\*.SEC" "%_Dest%" /L /Y') DO (
IF NOT EXIST "%_Dest%.\%%~nxa" ECHO %%~nxa
)
ECHO Refreshing...
ECHO Please wait %%a seconds...
%R1%
cls
)
::
:_Xfer
FOR /F %%a in ('xcopy "%_Monitor%\*.SEC" "%_Dest%" /L /Y') DO (
IF NOT EXIST "%_Dest%.\%%~nxa" xcopy "%%a" "%_Dest%" /Y
)
::
Echo.Change Detected
ECHO.
Goto :_StartMon
:::::::::::::::::::::::::::::::::::::::::::::::::::
:: Subroutine
:::::::::::::::::::::::::::::::::::::::::::::::::::
:_SetBaseline
If Exist "%temp%\tempfmstate.dir" Del "%temp%\tempfmstate.dir"
For /F "Tokens=* Delims=" %%I In ('Dir /S "%~2"') Do (
Set _Last=%%I
>>"%temp%\tempfmstate.dir" Echo.%%I
)
>"%~1" Findstr /V /C:"%_Last%" "%temp%\tempfmstate.dir"
Goto :EOF
Now, if you noticed the script will compare the content of source folder with the content of the destination folder and will copy the files from source if there is not already file in destination. I would like to know if someone can help me figuring out how to make the script to check the last modified file in source folder and ONLY copy it to the destination AND if the said file is currently in use/being write to then script will wait until it finished before trying to copy it again.
Also, right now I have to edit source.ini file to specify which folder I want to monitor and each time I have to change the subfolder path to watch for changes, is there a way to watch parent folder for any new files and take that file to the destination?
I found this script on the net but hoping to check the file not based on day but instead of time.
::Copy Files Made Or Modified Today
#echo off
setlocal
set source= <source directory>
set dest= <destination directory>
pushd "%source%"
set t=%date:~4%
::for /f %%a in ('dir /b /a-d /o-d') do call :PROCESS "%%a"
goto :eof
popd
:PROCESS
for /f %%j in ('echo %~t1') do set d=%%j
if "%d%"=="%t%" copy %1 "%dest%"
goto :eof
Hope to find the solution from here.
Many thanks
With this you'll get the last modified file:
for /f %%A in ('dir /b /tw /o-d not-mandatory-file-mask') do (
set last_file=%%A
goto :endloop
)
:endloop
With this you'll copy the file if it is possible. If not it will try again and again:
:tryagain
set /a errorlevel=0
copy some_dir\%last_file% %destination%
if %errorlevel% NEQ 0 (
sleep 10
goto :tryagain
) else (
goto :end
)
:end

Resources