Running a Command Against a List of Machines in Limited Parallel
I've often had the need to take a list of computers or servers and run the same command against all of them, and then take some result from that command to report back on. For example, I may want to ping a list of machines and log the response times, or perhaps check the status of BitLocker using manage-bde. Typically, I would have two ways to do this:
The serial batch file method
In this method, you would create a batch file that did what you wanted and the command would get run against each machine one at a time. Maybe I generated the list using Excel and had a file filled with "ping ComputerA >> ComputerA.txt" (and so on for each computer), or maybe I put the list in a text file and used a FOR loop to loop over each one. The pro of this approach is that it's easy, but the con is that if it's a long list, it's going to take a long time doing one computer at a time.
The Start.exe No-Wait Method
Using this method, you're doing the same this as above, but I'm calling something like "start cmd.exe /c ping ComputerA" (without the /wait switch). The pro of this method is that it will run as many processes as my computer can handle at once, but the huge con is that I now have potentially hundreds of processes spawning at once, over and over again, and this often leads to either really poor performance, or a crash.
Now... Slowing Down the Start.exe No-Wait Method
Because I want to still take the action on as many systems as I can at a time, but I don't want to flood my process list, I wrote this recently to help slow it all down. This batch file uses ping.exe as an example, but it could be anything else you'd like. It looks at the number of times a process already exists before starting a new one, and if it meets a preset count, we use the ping-localhost trick to delay the start of the next process.
1@echo off 2setLocal EnableDelayedExpansion 3 4set maximumprocesses=30 5set sleeptime=10 6 7for /F %%C in (computerlst.txt) do ( 8 echo Machine %%C 9 start /min cmd /c ping %%C ^> c:\\temp\\ping-%%C.txt 10 for /f "tokens=1,\*" %%P in ('tasklist ^| find /i /c "ping"') do set proclist=%%P 11 echo Running Process Count: !proclist! 12 if !proclist! GEQ %maximumprocesses% ( 13 echo Sleeping... 14 ping -n %sleeptime% 127.0.0.1 > NUL 15 ) 16 echo. 17)
To use this batch file, you'll need to:
- Save the text in a batch file (file with a .cmd or .bat extension)
- If you'd like, change the maximumprocesses (line 4) and sleeptime (line 5) values
- Change the command that the START command is running if you're not doing a ping (line 9)
- Change the process name that tasklist is looking for (line 10)
Now, a couple of notes about this batch file:
- We could probably get a bit better by introducing a real sleep (like sleep.exe), but I'm trying to keep the file easily movable. Ping is good enough.
- We're not really setting a maximum number of processes, we're just telling it when to sleep. If the EXE took a long time to complete, more than the set amount of processes would start. I feel this solution is "good enough", however, for most use cases.