Limit number of batch files launched by FOR loop - windows

I have a batch file that downloads 21 separate files (all very large). I have timeouts to try to allow some files to finish before trying to get more (not a nice solution), and if they all run together it uses all my system resources and everything grinds to a halt.
I can put /WAIT in my FOR loop, then it will only launch one download task at a time. I would like to launch 5 download windows, and once they are finished, continue with the next 5. I've tried to use GOTO to wait until finished but it does not work.
rem *****Step 1*****
set tstart=0
set tend=5
set incr=1
FOR /L %%a IN (%tstart%,%incr%,%tend%) DO (start /ABOVENORMAL GET_PIECE_ENSEMBLE.bat %newformatdate% 0%%a %HH%)
timeout /t 30
:WAIT
if exist flagfile.* goto WAIT
Then I have 4 more steps to download files 5-10, 11-16, etc. But if it takes extra long to download, the script moves along and starts downloading more files or moves on to the next processes, which require all the data to be downloaded first. I am trying 2 things here: a short timeout to ensure the data has started downloading (so a flagfile exists), and a wait if flagfile exists. But the wait isn't working, the script still moves along and starts downloading the next bunch. My workaround has been to set a timeout of 600 but sometimes the data takes more than 10 min to download, sometimes it hangs and takes up to two hours, sometimes only a few minutes. If all the pieces launch at once, it freezes up the workstation and we have to restart the machine.
And here is the code of GET_PIECE_ENSEMBLE, the script that runs in the FOR loop:
set "newformatdate=%1"
set "ename=%2"
set "HH=%3"
echo %time% > flagfile.%ename%
wget -O C:/Data/DataFile_%HH%.%ename%.grb2 http://url/%newformatdate%.grib2
del flagfile.%ename%
Looking for advice on either how to make the "wait if flagfile exists" work (any idea why this isn't working?), or if there is some simple argument I can put in my FOR loop to limit the number of instances so downloads finish before others start? Surely there is a better solution than guessing at a timeout interval when the network conditions vary so much!

In GET_PIECE_ENSEMBLE
try
...
set "HH=%3"
:only5
set /a running=0
for /f %%c in ('dir /b/a-d flagfile.* 2^>nul') do set /a running+=1
if %running% geq 5 (
timeout /t 1 >nul
goto only5
)
echo %time% > flagfile.%ename%
...
which should count the flagfiles and while there are 5 or more, delay 1 sec.
As soon as one flagfile is deleted, the next queued file to get activated will bump the number of in-process files to 5 again, blocking the other queued processes.

UNTESTED!!!
:: Start with list of files to download as parameters
:: down.cmd "file1" "file2" "file3" etc..
#echo off
set "url=http://host.com"
:loop
for /f %%a in ('tasklist /FI "imagename eq wget.exe" /FI "windowtitle eq _download*" /FO csv /NH ^| find /C "wget"') do (
if %%a LEQ 5 (
start "_download" "%path%\wget.exe" -O "%~1" "%url%/%~1"
shift
)
timeout /T 5
if not "%~1"=="" goto loop

Related

Second Timeout function didn`t work in Batch File

Working on a startup File (like a shortcut with special functions) for a game. (Counter-Strike Global Offensive)
The intention is to replace the menu music - some updates ago this was easier but right now I need to forging on a Batch file.
This is how it looks so far
#Echo off
"C:\Users\Cedo\Desktop\csgo\csgo\Counter-Strike Global Offensive.url"
Timeout 9
"C:\Users\Cedo\Desktop\csgo\csgo\csgomusic.m3u8"
[Timeout 240]!
[Taskkill /IM winamp.exe]! ----> Dont work!
Exit
and the way to here is more complicated than I thought. I solved already a lot of problems because a lot of the stuff on the internet seems outdated I guess.
The actual problem is a second timeout and taskkill for the winamp music file.
Strangely when inserting a second timeout (like the first) nothing happens.
Just after closing winamp manually the Timer starts!
But something needs to be done so that the second timeout with 240 secs and the taskkill to close winamp automatically works.
Another cool thing would be to start the winamp music file a bit later without minimizing the started game window.
(Already tried start /min commands - it doesn't matter actually - wont work if minimized or maximized)
Does someone know more? Right now I'm good with 9 Seconds(winamp opens before the game maximizes because this takes some time and so i didn`t get tabbed out).
And is there maybe a better way to build this up or improve things?
Just after closing winamp manually the Timer starts!
Given this statement I infer that "C:\Users\Cedo\Desktop\csgo\csgo\csgomusic.m3u8" is starting Winamp.
Note place the following script into the "C:\Users\Cedo\Desktop\csgo\csgo\" folder, or update the "_RootFolder" to have the correct path.
So essentially what you need to do is this:
Start a separate command window to run the task to kill winamp, because otherwise you will only move on tot the next command when Winamp is killed manually.
#(SETLOCAL
REM SET "KillCmd="%~f0" :Kill_Winamp"
REM SET "_StartKillCMD=start "Kill Winamp!" cmd /c %KillCmd%"
SET "_StartKillCMD=start "Kill Winamp!" cmd /c "%~f0" :Kill_Winamp"
SETLOCAL EnableDelayedExpansion
ECHO OFF
SET "eLvl=0"
REM SET "_RootFolder=%USERPROFILE%\Desktop\csgo\csgo\"
SET "_RootFolder=%~dp0"
SET "CSGo_URL=!_RootFolder!Counter-Strike Global Offensive.url"
SET "Music_File=!_RootFolder!csgomusic.m3u8"
SET /A "_Timer_WinAmp_Start=9", "_Timer_WinAmp_Kill= _Timer_WinAmp_Start + 240"
)
ECHO=%* | FIND /I ":Kill_Winamp" && (
CALL :Kill_Winamp
) || (
CALL :Main
)
( ENDLOCAL
Exit /b %_eLvl%
}
:Main
COLOR 2F
ECHO=======================================================================
ECHO= Launching CSGO, Waiting %_Timer_WinAmp_Start% seconds before starting Winamp.
ECHO=======================================================================
ECHO=
"%CSGo_URL%"
ECHO=
SETLOCAL DisableDelayedExpansion
%_StartKillCMD%
ECHO=
ECHO=Started Kill Timer..
ECHO=
Timeout %_Timer_WinAmp_Start%
"%Music_File%"
ECHO=Exiting, Remove the pause on the next line if not needed.
Pause
GOTO :EOF
:Kill_Winamp
COLOR 4F
ECHO=======================================================================
ECHO= Waiting %_Timer_WinAmp_Kill% Seconds before Killing Winamp.
ECHO=======================================================================
ECHO=
ECHO=
Timeout %_Timer_WinAmp_Kill%
Taskkill /IM winamp.exe
ECHO=Exiting, Remove the pause on the next line if not needed.
Pause
GOTO :EOF

How to find out when a program was started and then closed via cmd batch file and then create a rem on program close

I want to keep this simple. I have a ACCESS DB batch file that I want to run from a trusted users computer, or via task scheduler. Batchrun.mdb runs its queries and then exits automatically. I'd like to know how we can tell when it actually closes -- if and when it does i'd like to add a rem line that says update complete or similar with the time. Thank you! I cannot find anything on this via google. The key is knowing that the program actually opened, and closed. I will remove the exit /b if i can get this to work correctly.
Batch file:
Start "" "E:\REDACTED\Batch Files\Batchrun.mdb"
Exit /b
For adding the REM within your code, you can do Echo Rem [%date% %time%] update complete>>"%~f0" - As posted by #LotPings - And can have the batch loop using tasklist to seach for your program. Upon terminating, it will end the loop and print the REM timestamp in your script and exit.
#ECHO OFF
#SETLOCAL EnableDelayedExpansion
Start "" "E:\REDACTED\Batch Files\Batchrun.mdb"
Echo Rem [%date% %time%] Application Opened>>"%~f0"
:ProcessLoop
tasklist /FI "IMAGENAME eq Batchrun.mdb" 2>NUL | find /I /N "Batchrun.mdb">NUL
if "%ERRORLEVEL%"=="0" (GOTO :ProcessLoop) ELSE (GOTO :Closed)
:Closed
Echo Rem [%date% %time%] Application Closed>>"%~f0"
Exit /b
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: LOG

Batch File to Loop a VBScript During Certain Times of the Day

I am very new to batch, so please be easy. That being said, I am attempting to write a batch file that every few minutes will execute a VBScript that updates information on our network. Currently I have:
#ECHO OFF
REM Run VBScript
GOTO SKIP01
:LOOP
wscript "C:\Users\Desktop\RenameMove.vbs"
GOTO SKIP01
:
:
:SKIP01
REM 3 Min Delay
PING 1.1.1.1 -n 10 -w 18000 >NUL
IF HOUR:%time:~0,5% <09:44
GOTO SKIP01
IF HOUR:%time:~0,5% >16:00
GOTO SKIP01
IF HOUR:%time:~0,5% >=09:45
GOTO LOOP
Currently the window will open for a minute or two, then it just closes. If I run the above after removing the first GOTO SKIP01, the VBScript executes perfectly. Then there is a delay and the window closes. I imagine that it is everything under :SKIP01 that is causing the problem. The function I am trying to achieve is for the .BAT file to continuously loop a delay between the hours of 16:01 - 09:44. Then run the VBScript every 3 minutes from 09:45 - 16:00. Searching the forums I have yet to find anything to help. As I said, I am very new to Batch.
RE: My last comment below:
#ECHO OFF
setlocal enableextensions disabledelayedexpansion
set delay=180
set "startTime=09:45"
set "endTime=16:00"
:LOOP
set "now=%time: =0%"
:: ECHO It's %now%, waiting %delay% seconds
echo %date% %time% >> log.txt
if "%now%" geq "%startTime%:00,00" (
GOTO LOOP2
)
:LOOP2
set "now=%time: =0%"
:: ECHO It's %now%, waiting %delay% seconds
echo %date% %time% >> log.txt
if "%now%" lss "%endTime%:00,00" (
:: ECHO Start MSAccess
GOTO CALLVBS
)
:WAIT
REM 10 second delay
PING 127.0.0.1 -n %delay% >REM
GOTO LOOP
:CALLVBS
echo Time to Move!
wscript "C:\Users\ljs\Desktop\Stock_Automation_DO_NOT_EDIT\RenameMove.vbs"
GOTO WAIT
I renamed some things so it made more sense to me, but here is a working version. there are variables at the top for the delay (in seconds) and the start and end time for the window. I also modified your ping delay to something more simple, in my opinion.
Remove the echos for production and add the vbscript file to the CALLVBS function.
#ECHO OFF
setlocal enableextensions disabledelayedexpansion
set delay=180
set "startTime=09:45"
set "endTime=16:00"
:LOOP
set "now=%time: =0%"
ECHO It's %now%, waiting %delay% seconds
if "%now%" geq "%startTime%:00,00" (
ECHO It's after %startTime%
if "%now%" lss "%endTime%:00,00" (
ECHO And it's before %endTime%
GOTO CALLVBS
)
)
:WAIT
REM 10 second delay
PING 127.0.0.1 -n %delay% >REM
GOTO LOOP
:CALLVBS
echo It's time!
REM Call VBS here
GOTO WAIT
Here's what's happening, as explained by someone who barely knows what he's talking about:
ABOUT GOTO
First you need to know that batch files process line-by-line from top to bottom, unless it encounters certain statements like if, for or goto, the last of which being the one we are concerned with here. If the interpreter encounters a GOTO command, it will go to the corresponding label and resume processing code line by line until it finds another GOTO or gets to the end of the file.
SIMPLE EXAMPLE
#echo off
GOTO :FRUITS
:COLORS
echo Red
echo Green
GOTO :END
:FRUITS
echo Apple
echo Banana
GOTO :COLORS
:END
echo Done!
This outputs the following:
Apple
Banana
Red
Green
Done!
BREAKDOWN
Set up variables
#ECHO OFF
setlocal enableextensions disabledelayedexpansion
set delay=180
set "startTime=09:45"
set "endTime=16:00"
This sets some settings and creates some variables for use later. The variables should be self explanatory but
I can elaborate if you want.
check the time
01. :LOOP
02. set "now=%time: =0%"
03. ECHO It's %now%, waiting %delay% seconds
04. if "%now%" geq "%startTime%:00,00" (
05. ECHO It's after %startTime%
06. if "%now%" lss "%endTime%:00,00" (
07. ECHO And it's before %endTime%
08. GOTO CALLVBS
09. )
10. )
This is our "loop". :LOOP denotes what is basically a labeled
section of code that we can go back to any time we want. I called it LOOP
because it is the section we are doing over and over. It may have been more
accurate to call it :CHECKTIME or something similar, as that's what it does.
The label means absolutely nothing to the interpreter so calling it "LOOP"
doesn't mean it's going to repeat. This may be the biggest source of confusion.
Here is a
step-by-step of what each line on this block does (not processing
conditions, just line by line):
Get the current time.
Output the current time
Compare the current time to the endTime variable.
Output the result.
Compare the current time to the startTimevariable.
Output the result.
Goto the CALLVBS section of code
Note that I could have put GOTO WAIT at the end of this block and that might make more sense, but since :WAIT is the next block of
code that's what will process next anyway, so the GOTO would be
superfluous! This may be a second point of confusion.
Wait a bit
:WAIT
REM 10 second delay
PING 127.0.0.1 -n %delay% >REM
GOTO LOOP
This is the section of code that simply waits the specified number of
seconds. It does this using the ping command which is common for
batch programming since there is no built-in delay or sleep command
like other languages have. The first line is simply a comment, REM
means "Remove" (I think) and is how you comment out lines of code in
batch. As a matter of fact, I should have removed this since it's
not 10 seconds anyway :). The second line pings the localhost 180
times (or whatever the delay variable is set to). The >REM part
means it outputs the results of the ping to, well, I've never seen
"REM" here. Usually you would output it to nul but either way, it's
making sure you don't see the 180 ping results. Now, the 3rd line
tells the processor to go back to the :LOOP label. No matter what.
After the ping, it does the :LOOP section code again.
Execute your vbscript
:CALLVBS
echo It's time!
REM Call VBS here
GOTO WAIT
This is the :CALLVBS section of code. First it outputs "It's
time!". The second line is again, a comment. As you know, you
replace this with your vbscript. After this, the interpreter is told
to go to the :WAIT section of code. Again, it will always do this,
no matter what, after executing the line above it.

Possible to launch multiple threads of commands in cmd?

I have about 290 files that I need to optimize in a short period of time.
When I do optipng *.png it takes about 10 minutes to complete the transaction.
However when I do optipng a*.png and optipng m*.png in two separate command line it gets the work done in 5 minutes.
Now is there a way I can launch about 20 processes at the same time which will get the work done faster and not take up all of the space on my Desktop?
I have written a batch file that executes only a maximum number of commands a while ago: Parallel execution of shell processes:
#echo off
for /l %%i in (1,1,20) do call :loop %%i
goto :eof
:loop
call :checkinstances
if %INSTANCES% LSS 5 (
rem just a dummy program that waits instead of doing useful stuff
rem but suffices for now
echo Starting processing instance for %1
start /min wait.exe 5 sec
goto :eof
)
rem wait a second, can be adjusted with -w (-n 2 because the first ping returns immediately;
rem otherwise just use an address that's unused and -n 1)
echo Waiting for instances to close ...
ping -n 2 ::1 >nul 2>&1
rem jump back to see whether we can spawn a new process now
goto loop
goto :eof
:checkinstances
rem this could probably be done better. But INSTANCES should contain the number of running instances afterwards.
for /f "usebackq" %%t in (`tasklist /fo csv /fi "imagename eq wait.exe"^|wc -l`) do set INSTANCES=%%t
goto :eof
It spawns a maximum of four new processes that execute in parallel and minimized. Wait time needs to be adjusted probably, depending on how much each process does and how long it is running. You probably also need to adjust the process name for which tasklist is looking if you're doing something else.
There is no way to properly count the processes that are spawned by this batch, though. One way would be to create a random number at the start of the batch (%RANDOM%) and create a helper batch that does the processing (or spawns the processing program) but which can set its window title to a parameter:
#echo off
title %1
"%2" "%3"
This would be a simple batch that sets its title to the first parameter and then runs the second parameter with the third as argument. You can then filter in tasklist by selecting only processes with the specified window title (tasklist /fi "windowtitle eq ..."). This should work fairly reliable and prevents too many false positives. Searching for cmd.exe would be a bad idea if you still have some instances running, as that limits your pool of worker processes.
You can use %NUMBER_OF_PROCESSORS% to create a sensible default of how many instances to spawn.
You can also easily adapt this to use psexec to spawn the processes remotely (but wouldn't be very viable as you have to have admin privileges on the other machine as well as provide the password in the batch). You would have to use process names for filtering then, though.
Looks like you can write a batch file and run your commands asynchronously from that file.
Running Windows batch file commands asynchronously

Windows batch file - check if file has been modified

I'm configuring a windows machine to continuously run a powerpoint presentation. The ppt file is located on a samba share (windows file sharing) and will be updated periodically. I need a way to make a batch file that will restart the ppt slide show if the file has been changed. The batch script will run on a a regular interval, and will hopefully check to see if the file has been updated, and re-start the slideshow if it has.
If there is a way to do this in powershell, that would work too.
Well, in a batch, the easiest way I would think of would be to periodically check whether the file's last modification date and/or its size has changed.
You can get both via the following:
for %%X in (myfile) do set size=%%~zX&set filetime=%%~tX
You can then cache those values and compare whether they have changed since the last iteration. Using a delay (a few seconds maybe) via
ping -n 11 localhost >nul 2>nul
(for 10 seconds delay) can help in not checking too often.
So, it might look a little like the following:
#echo off
setlocal
set FileName=MyPresentation.pptx
set FileTime=-
:loop
for %%X in (%FileName%) do (
if %FileTime% NEQ %%~tX (
rem just an example
taskkill /f powerpnt.exe
start %FileName%
)
set FileTime=%%~tX
)
rem wait 5 seconds before checking again
ping -n 6 localhost >nul 2>nul
goto :loop
In PowerShell the code wouldn't look too different, except that you get to the relevant properties a little easier:
$FileName = "MyPresentation.pptx"
$FileTime = Get-Date
# endless loop
for () {
$file = Get-Item $FileName
if ($FileTime -ne $file.LastWriteTime) {
Get-Process powerpnt* | Stop-Process
Invoke-Item $file
}
$FileTime = $file.LastWriteTime
Start-Sleep 5
}
Try the code below. It checks for the current time and checks when the file was last modified. If the two values are the same it is assumed the file was altered.
#echo off &setlocal
TITLE File Monitor
Set file=Test.txt
:CheckforAlter
Set modif_time=
Set allm=
cls
for /f "tokens=1-3 delims=:,. " %%A in ("%time%") do (
set "Hour=%%A"
set "Min=%%B"
set "Sec=%%C"
)
set /a Hour = Hour %% 12
if %Hour%==0 set "Hour=12"
set "Allm=%Hour%:%Min%:%Sec%"
for /f %%i in ('"forfiles /m %file% /c "cmd /c echo #ftime" "') do set modif_time=%%i
echo.
echo.
IF %modif_time%==%Allm% (
echo File was altered.
start "" "%file%"
Timeout /t 1 >nul
)
echo file wasn't modified.
GOTO CheckforAlter
Found this:
#echo off
if not "%~1"=="" echo Modified date of %~1 is %~t1
on Experts exchange. Maybe a good point to start from.

Resources