This question already has answers here:
Start multiple tasks in parallel and wait for them in windows?
(2 answers)
Closed 2 years ago.
I have a batch scripts that starts multiple processes (partly other batch files) in background via
start /b /min command1.exe some params
start /b /min command2.bat some params
start /b /min command3.exe some params
at the end of the top level batch script I would like to wait until all processes are completed.
Is there any way to achieve this without implementing special means within the called programs?
You can create special batch file that runs another process, waits for it to finish and creates a special file. Then launch desired processes with this script without wait and loop until all files are created.
Sample code for inspiration:
Runner.bat:
:: Run, wait and create file; %1 = file name, %2..N - process and params
set FileName=%1
shift
start /b /min /wait %* && (echo OK > %FileName%) || (echo Fail > %FileName%)
Main.bat:
call Runner.bat No1 command1.exe some params
call Runner.bat No2 command2.bat some params
...
:Check
... if not exist "No1", "No2", ... (
timeout /t 5
goto :Check
)
One option is to try to start them with WMIC process call create "c:\some.exe","c:\exec_dir" and assign the created PID to variable and checking periodically that the PIDs are existing.
You can try also with :
(
start "" /b command1
start "" /b command2
)|findstr "^"
echo all processes in the brackets block have ended
but if some of the commands is bat file you explicitly'll have to use exit 0 without /b switch.
Related
I've got a batch script start.bat that creates a locked .lock file (basically, to verify if the script is running, by attempting to delete it) and starts a bunch of secondary looping batch scripts (they keep on running after start.bat is closed). The problem is when start.bat is closed the locked file remains locked, until ALL of the secondary scripts are closed.
Question: Are there any alternative methods to run secondary batch scripts without locking up the primary script until secondary ones are finished?
I feel like most of this code is irrelevant, but included it in case somebody wants to test it.
#echo off
set "started="
<nul >"%~nx0.lock" set /p ".=." ::Rewrite lock file with a single dot
2>nul (
9>>"%~f0.lock" (
set "started=1"
call :start
)
)
#if defined started (
del "%~f0.lock">nul 2>nul
) else (
exit //script closes
)
exit /b
:start
//irrelevant loop logic
Start pause.bat //Pause command to keep pause.bat open
//starts other batch files too
Seems like the file may still be in use?
Try:
del /F "%~f0.lock">nul 2>nul
Your problem are inherited handles. When you start a process with a redirection active, the process inherits the redirection. So, you need to keep the lock but start the processes while not keeping it.
You can try some variation of this
#echo off
setlocal enableextensions disabledelayedexpansion
rem Check if this is a lock instance
if "%~1"==".LOCK." goto :eof
rem Retrieve all the needed data to handle locking
call :getCurrentFile f0
for %%a in ("%f0%") do set "lockFile=%%~nxa.lock"
set "lockID=%random%%random%%random%%random%%random%%random%%random%"
rem Try to adquire lock
set "started="
2>nul (
>"%lockFile%" (
rem We get the lock - start a hidden instance to maintain the lock
set "started=1"
start "" /b cmd /k""%f0%" .LOCK. %lockID%"
)
)
rem Check if the lock was sucessful
if not defined started (
echo lock failed
pause
goto :eof
)
rem Launch the child processes, now detached from lock, as this cmd instance
rem is not holding it. The hidden cmd /k instance holds the lock
start "" write.exe
start "" notepad.exe
start "" /wait winver.exe
rem Once done, release the locked instance
>nul 2>nul (
wmic process where "name='cmd.exe' and commandline like '%%.LOCK. %lockid%%%'" call terminate
)
rem And remove the lock file
del "%lockFile%"
rem Done
goto :eof
rem To prevent problems: http://stackoverflow.com/q/12141482/2861476
:getCurrentFile returnVar
set "%~1=%~f0"
goto :eof
As the hidden cmd instance is hosted inside the same console than the current batch file, if you close the console, the lock is released (but the lock file is not deleted)
I need to know how can I check in batch file if the second batch file which is opened in other command window has stopped (waiting for argument or process not successful).
#echo off
:loop
start /wait rapidminer-batch.bat -f C:\Users\AHM-PC\Documents\ccc.rmp
echo cmd stopped
pause
goto loop
When called batch ends, it returns a value to errorlevel. It works for call, don't know if for start too.
if %errorlevel% gtr 0 (
echo failed
) else (
echo success)
or call exit /b <number of error> in your called batch, to return specific value. Check exit for more details.
The normal method to provide interbatch communication is a flag file
Either you create a flag file in the main routine and wait for the started routine to delete it or wait until the started batch creates a file, and delete it.
eg
echo.>myflag.txt
start anotherbatch.bat
:loop
timeout /t 1 >nul
if exist myflag.txt goto loop
Here, the batch will wait until myflag.txt is deleted, which you do in the second batch. All you need is for the two routines to agree on a filename to use.
Basically I'm trying to read from a text file of servers, ping them continuously, and output a timestamp when each server has rebooted (find "Request timed out.")
for /f "delims=" %%a in (Servers_List.txt) do (
start cmd /k ping -t %%a | find "Request timed out."
) && (Echo %%a rebooted at %time%.)
It launches each server ping -t in a separate window until I add the | find piece, and then it only launches one at a time after each subsequent window is closed.
Does anyone know a way to run each CMD window simultaneously without needing to close each prior window?
Thank you!
Your design cannot work :-(
Your first problem is you are piping the output of your START command, but you want to pipe the output of PING. That can be fixed by escaping the pipe as ^|.
You have a similar problem with your conditional command concatenation - you would need ^&^& instead of &&1.
But the biggest problem is your ECHO will not execute until the pipe is closed, which is never! FIND will continue to look for additional timeout lines after the first one is detected. It will not end until after the pipe is closed, and your ECHO will not run until after FIND terminates.
Your best bet would be to write a VBScript, or JScript, or PowerShell script to continuously ping a server, and write out your message with timestamp when timeout is detected.
You could write a batch script to loop through your list of servers and start a new custom ping process for each one. Or you could put everything into a single VBScript (or JScript, or Powershell) script.
Perhaps one of the following links can help get you started:
http://windowsitpro.com/scripting/how-can-i-use-vbscript-script-ping-machine
http://www.sems.org/2013/07/little-vbscript-to-continously-ping-a-host-with-timestamplog/
https://thwack.solarwinds.com/docs/DOC-135033
I am assuming you ideally would like to have all output in a single screen. You can use the START /B option to run multiple processes within the same console window, but then you run the risk of garbled output if two processes attempt to write to the console at the same time. This can be solved via the exclusive lock that is obtained when a process opens a file for writing. See How do you have shared log files under Windows? for some pointers on how this can help. That link is for shared log files, but it is easily adapted for shared console access.
I have used my JREPL.BAT utility to hack up a solution. Architecturally this is a convoluted abomination, but it works! It runs an endless PING process for each server, all in parallel within the same console. Each process pipes its output to JREPL which detects if the server responded and writes out a time-stamped message to both the console and a log file. The log file is also used as a lock file to coordinated shared access between the processes. Each process keeps track of the last server status, and a message is only written when the status changes.
#if (#X)==(#Y) #end /* Harmless hybrid line that begins a JScript comment
::***** Batch code *******
#echo off
setlocal enableDelayedExpansion
set "first="
copy nul serverStatus.log >nul
for /f "delims=" %%A in (Servers_List.txt) do if not defined first (
set "first=%%A"
) else (
start /b cmd /c ping -t %%A ^| jrepl "^Request timed out|^Reply from " "log('%%A','DOWN')|log('%%A','UP')" /t "|" /jmatch /jlib "%~f0"
)
if defined first ping -t %first% | jrepl "^Request timed out|^Reply from " "log('%first%','DOWN')|log('%first%','UP')" /t "|" /jmatch /a /jlib "%~f0"
exit /b
****** JScript code ******/
var status = 'initial';
var fso = new ActiveXObject("Scripting.FileSystemObject");
var file;
function log( server, newStatus ) {
if (status!=newStatus) {
status=newStatus;
var msg=new Date().toString()+' '+server+' is '+newStatus;
while (!openFile());
file.WriteLine(msg);
output.WriteLine(msg);
file.Close();
}
return false;
}
function openFile() {
try {
file = fso.OpenTextFile('serverStatus.log',8);
return true;
} catch(e) {
return false;
}
}
But, I don't see why it is necessary to run the processes in parallel. It seems to me you can simply enter an endless loop that cycles through the servers, and PINGs each server individually, writing out the status. Here is a simple batch script that does just that. Again, it only prints out a message when it detects a change in status.
#echo off
setlocal enableDelayedExpansion
for /l %%N in () do for /f "delims=" %%S in (Servers_List.txt) do (
ping /n 1 %%S | findstr /bc:"Request timed out" >nul && set "status='DOWN'" || SET "status=UP"
if "!status!" neq "!%%S!" (
set "%%S=!status!"
echo !date! !time! %%S is !status!
)
)
I am running 1 st batch utility calling another 2nd batch. The 2nd batch calls a setup.cmd command which has internal Java code to patch files.
When I just call the 2nd batch from 1st batch --
1st batch calls the second batch
2nd batch calls the setup.cmd
1st batch continues with further code without waiting for setup.cmd to complete.
I tried using start /wait to call the setup.cmd but that does not returns back the control to 1st batch. It keeps the session after installing.
1st batch calls 2nd batch by using CALL
2nd batch has following code to call setup.cmd
%windir%\system32\cmd /c start /WAIT Disk1\setup.cmd %parameter%
How can I get the control back to 1st batch once the setup.cmd completes?
If you just use this without the start command, and call batch2 then it will wait until it is finished.
call Disk1\setup.cmd %parameter%
Resolved this last week by using loops and by calling the setup.cmd...sorry for delay in this post.
#echo off
CALL \Installers\Disk1\setup.cmd -i silent -FILE=\Silent\Silent.txt
:LOOP
tasklist /FI "username eq SOMEUSER" 2>NUL | find /I /N "java">NUL
ECHO %ERRORLEVEL%
if "%ERRORLEVEL%"=="1" (
GOTO CONTINUE
) ELSE (
ECHO PATCH is still running, Sleeping for 5 Mins
SLEEP 300
GOTO LOOP
)
:CONTINUE
I'd like to do something like:
start program1 ^>output
start program2 ^>output
and order the output so that the output is ordered. I don't care which output is first (program1's or program2's), but I'd like it to be a complete dump of that program's output.
Ideally, I'd like to run multiple programs in parallel with output all going to the screen, but a file would be fine. I don't need to see it while it is running, just have it intelligible when it's finished.
EDIT:
If I follow the suggestion to output them to a file then merge them when it's done, I have the trouble of waiting 'til all the programs are done -- so, I need a waitUntilAllTheStartsAreFinished command.
start program1 ^>output1
start program2 ^>output2
#... as many as I need
waitUntilAllTheStartsAreFinished
TYPE output1
TYPE output2
delete output1
delete output2
You can use a loop and tasklist:
:wait
rem just wait a second before looking again
ping -n 2 ::1 >nul 2>nul
tasklist 2>&1 | findstr /b "program1.exe program2.exe ..." >nul 2>&1 && goto wait
It will only continue further until all of program1.exe program2.exe ... are terminated.
Joey's suggested method will work, but it can become problematic if your programs can be launched multiple times. It becomes difficult to tell which tasks are the onces you want to monitor.
Each program will have an exclusive lock on the temporary output file until the program finishes. Any attempt by another process to redirect to the same file will fail. This can be used to detect when the program finishes.
I used TIMEOUT to insert a delay in the polling. If you are on a system like XP that does not have TIMEOUT then you can use ping -n 2 ::1 >nul 2>nul instead.
I've included extensive documentation on how this solution works in the code. Edit - I have simplified the code a bit by removing one unneccessary code block level, and I improved the documentation.
#echo off
setlocal
REM Define a base name for the temporary output files. I've incorporated
REM a random number in the file name to generally make it safe to run this
REM master script multiple times simultaneously. It is unlikely a collision
REM will occur, but incorporating a timestamp in the name would make it more
REM reliable.
set "baseName=%temp%\output%random%_"
set /a "progCount=2, completedCount=0"
REM Start each program with both stdout and stderr redirected to a temporary
REM ouptut file. The program will have an exclusive lock on the output file
REM until it finishes executing. I've assumed the program is another batch file
REM and I use the START /B switch so that the programs are run in the same
REM window as this master script. Any console program will work, and the
REM /B switch is optional.
start /b "" ^"cmd /c test.bat ^>"%baseName%1" 2^>^&1^"
start /b "" ^"cmd /c test2.bat ^>"%baseName%2" 2^>^&1^"
REM etc.
REM Clear any existing completed flags, just in case
for /l %%N in (1 1 %progCount%) do set "completed%%N="
:loopUntilDone
REM Introduce a delay so we don't inundate the CPU while we poll
timeout /nobreak 1 >nul
REM Loop through each of the output file numbers.
REM Redirect the stderr for the DO block to nul so that if the inner
REM block redirection fails, the error message will be suppressed.
for /l %%N in (1 1 %progCount%) do (
REM Only test this particular program if the output file has been
REM created (in other words, the program has started) and we haven't
REM already detected that it has finished. Also redirect an unused
REM file handle to the output file in append mode. The redirection will
REM fail if the program has not completed. If the redirection fails then
REM the IF block is not executed.
if not defined completed%%N if exist "%baseName%%%N" (
REM We are within the block, meaning the redirection succeeded and
REM the program must have finished. So print out the results.
echo(
echo Ouput for program%%N
echo ---------------------------------------------
type "%baseName%%%N"
REM Set a flag so we know this program has finished
set completed%%N=1
REM Increment the completed count so we know when we are done
set /a completedCount+=1
) 9>>"%baseName%%%N"
) 2>nul
if %completedCount% neq %progCount% goto :loopUntilDone
del "%baseName%*"