Here is what I am trying to do. Suppose I have a program called myprogram.exe, which I have to execute 1000 times.
Under Windows, I could usually do something as simple as:
for /L %n in (1,1,1000) do start /myfolder/myprogram.exe
However, suppose I only have 5 CPU threads I can devote to running the 1000 instances of myprogram.exe, such that I launch only 5, then when one of these finishes another one is launched, etc until the whole 1000 end.
Under Linux and using GNU Parallel, I could simply do:
seq 1000 | parallel -N0 -j5 "nohup myprogram.exe"
How could I achieve something like that in Windows command line? Notice that in my case using Cygwin is not an option, so resorting to xargs and GNU Parallel under Windows are not options either.
Here is a way, by using powershell to do the process count. and using a simply set /a as counter.
#echo off
setlocal enabledelayedexpansion
set /a cnt=0
:counter
if !cnt! lss 1000 (
for /F "tokens=*" %%i in ('powershell ^(Get-Process -Name 'myprogram'^).count') do set proc=%%i
if !proc! lss 5 (
start "C:\myfolder\myprogram.exe"
set /a cnt+=1
)
goto :counter
)
You could add echo !cnt! in the line before goto :counter if you want to see it count.
It can be done without using delayedexpansion but I prefer to use it here.
This will run five processes in parallel. Each time one of them finishes, the next process will be started (so there are always 5 of them until they all are done)
#ECHO off
setlocal enabledelayedexpansion
set bunch=5
for /l %%a in (1,1,1000) do (
call :loop
echo processing: %%a
start "MyCommand" cmd /c timeout !random:~-1!
)
call :loop
goto :eof
:loop REM waits for available slot
for /f %%x in ('tasklist /fi "windowtitle eq MyCommand" ^| find /c "cmd.exe"') do set x=%%x
if %x% geq %bunch% goto :loop
goto :eof
Add the /min switch to start to minimize the started processes.
Give them a unique windowtitle (MyCommand here) to be able to count them.
Replace cmd /c timeout !random:~-1! with your actual command.
EDIT a slightly modified script, which may work better, if myprogram is a GUI (script above will work better with CLI applications):
#ECHO off
setlocal enabledelayedexpansion
set bunch=5
for /l %%a in (1,1,100) do (
call :loop
echo processing: %%a
start notepad.exe
)
call :loop
goto :eof
:loop REM waits for available slot
for /f %%x in ('tasklist /fi "imagename eq Notepad.exe"^|find /c "."') do set x=%%x
if %x% geq %bunch% goto :loop
goto :eof
Related
This question already has answers here:
Parallel execution of shell processes
(6 answers)
Closed 4 years ago.
I have this batch command
#echo off
FOR %%i in (C:\input\*.*) DO (
echo processing %%i
if not exist "C:\output\%%i" process.exe "%%i" -out "C:\output\%%i"
)
echo ---- finished ----
pause
Here my tool process.exe processes in a loop all files within a directory - if a result doesn't already exist.
Now my CPU is fast enough to run this process.exe on 2 or 3 files at the same time which would make the processing of the files much faster.
Question: How do I have to change the command to make my batch file processing 2-3 files at the same time?
The following starts processes up to a max count of %bunch%. Whenever one of them finishes, another one will be started.
#ECHO off
setlocal enabledelayedexpansion
set bunch=3
for %%a in (C:\input\*) do (
call :loop
echo processing: %%a
start "MyCommand" cmd /c timeout 60
REM if not exist "C:\output\%%i" start "MyCommand" cmd /c process.exe "%%i" -out "C:\output\%%i"
)
call :loop
goto :eof
:loop REM waits for available slot
echo on
for /f %%x in ('tasklist /fi "windowtitle eq MyCommand" ^| find /c "cmd.exe"') do set x=%%x
if %x% geq %bunch% goto :loop
echo off
goto :eof
I don't have your process.exe, so I have to guess. but the REMed line should work for you. (the timeout command is just to show the princip)
I have tried to make Windows batch file that blinking the word "Wait" & "Wait..". I tried the following code:
#Echo OFF
setlocal EnableDelayedExpansion
for /f %%a in ('copy /Z "%~f0" nul') do set "CR=%%a"
SET p=-1
set num=2
set st[1]=Wait
set st[2]=Wait..
set st[3]=eer
:LOOP
if /i %num% equ 1 (
set num=2
) else (
set num=1
)
<nul set /P "=!st[%num%]!!CR!"
TIMEOUT /T 1 >NUL
GOTO :LOOP
The problem here, the IF seems to be worked only one time. i.e running the batch makes it prompt "Wait" only one time, then "Wait.." forever. What is the mistake here?
You problem is not the if (that works as intended), your problem are the spaces that should delete/overwrite the dots.
set "st[1]=Wait "
set "st[2]=Wait.."
I have a situation very similar to the one described in this question (but in batch, not shell). I made a simple batch script to iterate through the lines of a tile and download data from a server using a python script (the process itself is more complicated than just a simple download, it has to authenticate with an API and fetch several URLs).
The first version was as follows:
for /F "tokens=*" %%A in (client_name_list.txt) do python download_metadata.py "%%A"
The way it is it waits until each iteration is done to move on, so I updated it to the following:
for /F "tokens=*" %%A in (client_name_list.txt) do start cmd /C python download_metadata.py "%%A"
The second versions does what I want to but, as the file client_name_list.txt is about 30,000 lines long, a lot of command prompts start spawning and the computers freezes within seconds.
How do I limit the number of running instances of CMD (to, for example 10) and make the script wait until there is a "free CMD slot" to go the next line?
Adapted from my answer to "Parallel execution of shell processes". Follow the link to get an explanation.
#echo off
setlocal enableDelayedExpansion
:: Display the output of each process if the /O option is used
:: else ignore the output of each process
if /i "%~1" equ "/O" (
set "lockHandle=1"
set "showOutput=1"
) else (
set "lockHandle=1^>nul 9"
set "showOutput="
)
:: Define the maximum number of parallel processes to run.
set "maxProc=10"
:: Get a unique base lock name for this particular instantiation.
:: Incorporate a timestamp from WMIC if possible, but don't fail if
:: WMIC not available. Also incorporate a random number.
set "lock="
for /f "skip=1 delims=-+ " %%T in ('2^>nul wmic os get localdatetime') do (
set "lock=%%T"
goto :break
)
:break
set "lock=%temp%\lock%lock%_%random%_"
:: Initialize the counters
set /a "startCount=0, endCount=0"
:: Clear any existing end flags
for /l %%N in (1 1 %maxProc%) do set "endProc%%N="
:: Launch the commands in a loop
set launch=1
for /f "delims=" %%A in (client_name_list.txt) do (
if !startCount! lss %maxProc% (
set /a "startCount+=1, nextProc=startCount"
) else (
call :wait
)
set cmd!nextProc!=%%A
if defined showOutput echo -------------------------------------------------------------------------------
echo !time! - proc!nextProc!: starting %%A
2>nul del %lock%!nextProc!
%= Redirect the lock handle to the lock file. The CMD process will =%
%= maintain an exclusive lock on the lock file until the process ends. =%
start /b "" cmd /c %lockHandle%^>"%lock%!nextProc!" 2^>^&1 python download_metadata.py "%%A"
)
set "launch="
:wait
:: Wait for procs to finish in a loop
:: If still launching then return as soon as a proc ends
:: else wait for all procs to finish
:: redirect stderr to null to suppress any error message if redirection
:: within the loop fails.
for /l %%N in (1 1 %startCount%) do 2>nul (
%= Redirect an unused file handle to the lock file. If the process is =%
%= still running then redirection will fail and the IF body will not run =%
if not defined endProc%%N if exist "%lock%%%N" 9>>"%lock%%%N" (
%= Made it inside the IF body so the process must have finished =%
if defined showOutput echo ===============================================================================
echo !time! - proc%%N: finished !cmd%%N!
if defined showOutput type "%lock%%%N"
if defined launch (
set nextProc=%%N
exit /b
)
set /a "endCount+=1, endProc%%N=1"
)
)
if %endCount% lss %startCount% (
1>nul 2>nul ping /n 2 ::1
goto :wait
)
2>nul del %lock%*
if defined showOutput echo ===============================================================================
echo Done
In each iteration of your for loop you can count the number of CMD task open. If the value is lesser than the limit you start a new task else you wait until one slot is free.
#echo off
set $Limit=11
setlocal enabledelayedexpansion
for /F "tokens=*" %%A in (client_name_list.txt) do (call:wait %%A)
exit/b
:wait
set "$cmd="
for /f %%a in ('tasklist ^| findstr /i "cmd"') do set /a $cmd+=1
if !$cmd! lss %$Limit% (
start cmd /C python download_metadata.py "%1"
goto:eof)
ping localhost -n 2 >nul
goto:wait
So far I've been trying to make a continuous .bat file that will start the server file, read every line that comes down and if the response "Server has become unresponsive" then the bat will close the file and re-open(this needs to be done every hour or so and I'm not always at the computer)
I do believe this is the correct code but I need to double check with some tech-savy minds to see if it's correct.
#echo off
SETLOCAL DisableDelayedExpansion
FOR /F "usebackq delims=" %%A in (`"findstr rust_server/n ^^ "`) do (
set "myVar=%%A"
call :processLine myVar
)
goto :eof
:processLine
SETLOCAL EnableDelayedExpansion
set "line=!%1!"
set "line=!line:*:=!"
echo(!line!
Find /I /V "Unresponsive for 10"
taskkill /fi "WindowTitle eq rust_server*"
start /d "C:\Rust Server" rust_server.exe
ENDLOCAL
goto :eof
Any ideas/suggestions would be greatly appreciated.
#Echo off
Title SERVER RESTARTER (place with your rust_server exe)
color 1f
SET n=0
:Loop
SET /A n=n+1
echo Server Restarter
echo -----Restart Server Batch VERSION-----
taskkill /IM rust_server.exe
echo Opening rust_server.exe server again
rust_server.exe
if %n% EQU 60 (
exit
) Else if %n% LEQ 24 (
Goto Loop
I want to write a batch file that updates a DLL that is in use by a running process, a regular application.
To do this, the plan is to stop the process, copy the DLL to the required location, then restart the process.
I know I can try to kill a process with taskkill. How can I make sure the process has fallen over and died, after I shoot it?
Here's what I used. It's a subroutine in a batch file.
set tasklist=%windir%\System32\tasklist.exe
set taskkill=%windir%\System32\taskkill.exe
-------------------------------------------------------
:STOPPROC
set wasStopped=0
set procFound=0
set notFound_result=ERROR:
set procName=%1
for /f "usebackq" %%A in (`%taskkill% /IM %procName%`) do (
if NOT %%A==%notFound_result% (set procFound=1)
)
if %procFound%==0 (
echo The process was not running.
goto :EOF
)
set wasStopped=1
set ignore_result=INFO:
:CHECKDEAD
"%windir%\system32\timeout.exe" 3 /NOBREAK
for /f "usebackq" %%A in (`%tasklist% /nh /fi "imagename eq %procName%"`) do (
if not %%A==%ignore_result% (goto :CHECKDEAD)
)
goto :EOF
-------------------------------------------------------
To use it from within a batch file, do like this:
call :STOPPROC notepad.exe
Full example:
set tasklist=%windir%\System32\tasklist.exe
set taskkill=%windir%\System32\taskkill.exe
-------------------------------------------------------
:STOPPROC
set wasStopped=0
set procFound=0
set notFound_result=ERROR:
set procName=%1
for /f "usebackq" %%A in (`%taskkill% /IM %procName%`) do (
if NOT %%A==%notFound_result% (set procFound=1)
)
if %procFound%==0 (
echo The process was not running.
goto :EOF
)
set wasStopped=1
set ignore_result=INFO:
:CHECKDEAD
"%windir%\system32\timeout.exe" 3 /NOBREAK
for /f "usebackq" %%A in (`%tasklist% /nh /fi "imagename eq %procName%"`) do (
if not %%A==%ignore_result% (goto :CHECKDEAD)
)
goto :EOF
-------------------------------------------------------
:MAIN
call :STOPPROC notepad.exe
call :STOPPROC Skype.exe
You'll notice lines that have all dashes - that's not a legal syntax for a batch file of course. But, those lines are never reached, because of the use of GOTO statements, so the syntax is never evaluated. Therefore those lines aren't a problem.