How to wait all batch files to finish before exiting? - windows

I have a main batch file than calls 4 other batch files so we can run in parallel.
Example:
Main.bat
start call batch1.bat
start call batch2.bat
start call batch3.bat
start call batch4.bat
exit
I want the Main.bat to exit after all the batch1 to batch 4 has stopped executing. In this way, I can get the total run time of the batch file. Problem is Main.bat exits even before batch1 to batch4 finishes executing.
I tried to compute for %errorlevel% for each batch file, but it always return 0 even though the 4 .bat files are still running.
Hoping someone could help me!
Thank you! :)

I think this is the simplest and most efficient way:
#echo off
echo %time%
(
start call batch1.bat
start call batch2.bat
start call batch3.bat
start call batch4.bat
) | set /P "="
echo %time%
In this method the waiting state in the main file is event driven, so it does not consume any CPU time!
EDIT: Some explanations added
The set /P command would terminate when anyone of the commands in the ( block ) outputs a line, but start commands don't show any line in this cmd.exe. This way, set /P keeps waiting for input until all processes started by start commands ends. At that point the pipe line associated to the ( block ) is closed, so the set /P Stdin is closed and set /P command is terminated by the OS.

give a unique title string to the new processes, then check if any processes with this string in the title are running:
start "+++batch+++" batch1.bat
start "+++batch+++" batch2.bat
start "+++batch+++" batch3.bat
start "+++batch+++" batch4.bat
:loop
timeout /t 1 >nul
tasklist /fi "windowtitle eq +++batch+++*" |find "cmd.exe" >nul && goto :loop
echo all tasks finished
(find is used, because tasklist does not return a helpful errorlevel)

Give this a try.
#echo off
echo %time%
start "" /wait cmd /c bat1.bat |start "" /wait cmd /c bat2.bat |start "" /wait cmd /c bat3.bat
echo %time%
pause

You could have batch1..batchn4 create flag files when they finish running.
e.g echo y > flag1 in batch1.bat
Then in the main batch file check for the existence of the flag files before exiting. You would need some sort of sleep utility to do something like this at the end of the main batch file:
IF EXIST flag1 GOTO check2
sleep <for a short amount of time>
goto check1
:check2
IF EXIST flag2 GOTO check3
sleep <for a short amount of time>
goto check2
:check3
IF EXIST flag3 GOTO check4
sleep <for a short amount of time>
goto check3
:check4
IF EXIST flag4 GOTO xit
sleep <for a short amount of time>
goto check4
:xit
The downside of this technique is that your timing is going to be off a little because you're polling for the flag files instead of being event driven. This may or may not be a problem in your situation. Batch files are pretty limited in this way. You might be better off trying to do it in PowerShell or python or some other more capable scripting language.

No one mentioned this solution, which I find to be the best. Put this at the end of your script, modify your process name if needed and the search strings.
This includes modifications necessary to have this work remotely too which was a headache. Originally I used tasklist instead of WMIC and checked named window titles defined when START is called, but windowName is N/A in tasklist when using it remotely. The powershell command worked better than timeout in our circumstances, which required us to run from this from a Jenkins agent.
:WAIT_FOR_FINISH
ECHO Waiting...
powershell -command "Start-Sleep -Milliseconds 5000" > nul
WMIC PROCESS WHERE Name="cmd.exe" GET commandline | findstr "searchString1 searchString 2" > nul && goto :WAIT_FOR_FINISH
ECHO.
ECHO Processes are Complete!
ECHO.

Related

Windows Batch, how to pause then close window?

Windows batch file, I want the window close itself after user hit a key.
For example
#echo off
echo ---------------------
echo hit a key will close
echo ---------------------
pause
exit /b
but it won't close
You dont need to add echo hit a key will close; basically the pause
command already says that for you.
But if you want to add your own custom message you can add
pause>nul
and add an echo above that for a custom exit message,
then type
exit
after the pause >nul.
In the RPG I'm working on, I use a quitgame.bat as I also need to close other processes when the batch exits.
CHOICE is the command I use for most input, and I present the option to 'Q'uit at all times (single keypress to exit the game). when the user selects this Choice, the errorlevel check has the Batch go to a label to Call my quitgame.bat
Set ask=CHOICE /N /C:
Set then= /M ""
Set do=IF ERRORLEVEL ==
%ask%nqfmie%then%
%do%6 GOTO enterSL
%do%5 GOTO viewinv
%do%4 GOTO market
%do%3 GOTO forcereset
%do%2 GOTO quit
%do%1 GOTO nextpage
:quit
CALL "%Quitgame%"
In my quitprogram:
#ECHO OFF
REM program to taskkill vbs scripts and child processes
CALL "%killmusic%"
REM deletes all vbs scripts created during programs execution
DEL /Q %sounds%\*.vbs
REM closes the command console
EXIT
If you dont have other processes or multiple bat files to warrant calling another program, just use a label you can goto using choice.
:quit
Exit

Is there a way to run a batch of asynchronous commands a few at a time? [duplicate]

I have a main batch file than calls 4 other batch files so we can run in parallel.
Example:
Main.bat
start call batch1.bat
start call batch2.bat
start call batch3.bat
start call batch4.bat
exit
I want the Main.bat to exit after all the batch1 to batch 4 has stopped executing. In this way, I can get the total run time of the batch file. Problem is Main.bat exits even before batch1 to batch4 finishes executing.
I tried to compute for %errorlevel% for each batch file, but it always return 0 even though the 4 .bat files are still running.
Hoping someone could help me!
Thank you! :)
I think this is the simplest and most efficient way:
#echo off
echo %time%
(
start call batch1.bat
start call batch2.bat
start call batch3.bat
start call batch4.bat
) | set /P "="
echo %time%
In this method the waiting state in the main file is event driven, so it does not consume any CPU time!
EDIT: Some explanations added
The set /P command would terminate when anyone of the commands in the ( block ) outputs a line, but start commands don't show any line in this cmd.exe. This way, set /P keeps waiting for input until all processes started by start commands ends. At that point the pipe line associated to the ( block ) is closed, so the set /P Stdin is closed and set /P command is terminated by the OS.
give a unique title string to the new processes, then check if any processes with this string in the title are running:
start "+++batch+++" batch1.bat
start "+++batch+++" batch2.bat
start "+++batch+++" batch3.bat
start "+++batch+++" batch4.bat
:loop
timeout /t 1 >nul
tasklist /fi "windowtitle eq +++batch+++*" |find "cmd.exe" >nul && goto :loop
echo all tasks finished
(find is used, because tasklist does not return a helpful errorlevel)
Give this a try.
#echo off
echo %time%
start "" /wait cmd /c bat1.bat |start "" /wait cmd /c bat2.bat |start "" /wait cmd /c bat3.bat
echo %time%
pause
You could have batch1..batchn4 create flag files when they finish running.
e.g echo y > flag1 in batch1.bat
Then in the main batch file check for the existence of the flag files before exiting. You would need some sort of sleep utility to do something like this at the end of the main batch file:
IF EXIST flag1 GOTO check2
sleep <for a short amount of time>
goto check1
:check2
IF EXIST flag2 GOTO check3
sleep <for a short amount of time>
goto check2
:check3
IF EXIST flag3 GOTO check4
sleep <for a short amount of time>
goto check3
:check4
IF EXIST flag4 GOTO xit
sleep <for a short amount of time>
goto check4
:xit
The downside of this technique is that your timing is going to be off a little because you're polling for the flag files instead of being event driven. This may or may not be a problem in your situation. Batch files are pretty limited in this way. You might be better off trying to do it in PowerShell or python or some other more capable scripting language.
No one mentioned this solution, which I find to be the best. Put this at the end of your script, modify your process name if needed and the search strings.
This includes modifications necessary to have this work remotely too which was a headache. Originally I used tasklist instead of WMIC and checked named window titles defined when START is called, but windowName is N/A in tasklist when using it remotely. The powershell command worked better than timeout in our circumstances, which required us to run from this from a Jenkins agent.
:WAIT_FOR_FINISH
ECHO Waiting...
powershell -command "Start-Sleep -Milliseconds 5000" > nul
WMIC PROCESS WHERE Name="cmd.exe" GET commandline | findstr "searchString1 searchString 2" > nul && goto :WAIT_FOR_FINISH
ECHO.
ECHO Processes are Complete!
ECHO.

My batch file that runs other batch file won't wait before the first is finished (using /WAIT command). Can anyone help me do it? [duplicate]

I have a main batch file than calls 4 other batch files so we can run in parallel.
Example:
Main.bat
start call batch1.bat
start call batch2.bat
start call batch3.bat
start call batch4.bat
exit
I want the Main.bat to exit after all the batch1 to batch 4 has stopped executing. In this way, I can get the total run time of the batch file. Problem is Main.bat exits even before batch1 to batch4 finishes executing.
I tried to compute for %errorlevel% for each batch file, but it always return 0 even though the 4 .bat files are still running.
Hoping someone could help me!
Thank you! :)
I think this is the simplest and most efficient way:
#echo off
echo %time%
(
start call batch1.bat
start call batch2.bat
start call batch3.bat
start call batch4.bat
) | set /P "="
echo %time%
In this method the waiting state in the main file is event driven, so it does not consume any CPU time!
EDIT: Some explanations added
The set /P command would terminate when anyone of the commands in the ( block ) outputs a line, but start commands don't show any line in this cmd.exe. This way, set /P keeps waiting for input until all processes started by start commands ends. At that point the pipe line associated to the ( block ) is closed, so the set /P Stdin is closed and set /P command is terminated by the OS.
give a unique title string to the new processes, then check if any processes with this string in the title are running:
start "+++batch+++" batch1.bat
start "+++batch+++" batch2.bat
start "+++batch+++" batch3.bat
start "+++batch+++" batch4.bat
:loop
timeout /t 1 >nul
tasklist /fi "windowtitle eq +++batch+++*" |find "cmd.exe" >nul && goto :loop
echo all tasks finished
(find is used, because tasklist does not return a helpful errorlevel)
Give this a try.
#echo off
echo %time%
start "" /wait cmd /c bat1.bat |start "" /wait cmd /c bat2.bat |start "" /wait cmd /c bat3.bat
echo %time%
pause
You could have batch1..batchn4 create flag files when they finish running.
e.g echo y > flag1 in batch1.bat
Then in the main batch file check for the existence of the flag files before exiting. You would need some sort of sleep utility to do something like this at the end of the main batch file:
IF EXIST flag1 GOTO check2
sleep <for a short amount of time>
goto check1
:check2
IF EXIST flag2 GOTO check3
sleep <for a short amount of time>
goto check2
:check3
IF EXIST flag3 GOTO check4
sleep <for a short amount of time>
goto check3
:check4
IF EXIST flag4 GOTO xit
sleep <for a short amount of time>
goto check4
:xit
The downside of this technique is that your timing is going to be off a little because you're polling for the flag files instead of being event driven. This may or may not be a problem in your situation. Batch files are pretty limited in this way. You might be better off trying to do it in PowerShell or python or some other more capable scripting language.
No one mentioned this solution, which I find to be the best. Put this at the end of your script, modify your process name if needed and the search strings.
This includes modifications necessary to have this work remotely too which was a headache. Originally I used tasklist instead of WMIC and checked named window titles defined when START is called, but windowName is N/A in tasklist when using it remotely. The powershell command worked better than timeout in our circumstances, which required us to run from this from a Jenkins agent.
:WAIT_FOR_FINISH
ECHO Waiting...
powershell -command "Start-Sleep -Milliseconds 5000" > nul
WMIC PROCESS WHERE Name="cmd.exe" GET commandline | findstr "searchString1 searchString 2" > nul && goto :WAIT_FOR_FINISH
ECHO.
ECHO Processes are Complete!
ECHO.

Batch File calling Console Application - Leaves CMD window open

I am calling a C# Console Application via batch file, in order to send the application output into a text file, with the date/time etc.
The problem I have is that when the console application completes, it leaves the batch window open, because there is a PAUSE (the C# equivalent), so a key must be pressed for the window to close. This means I do not know when the job has finished.
Is there a way I can make the CMD window close when the application finished, without having to change the C# Application code?
#ECHO================================================================================
#ECHO The Application is currently running and may take some time. Please wait...
#ECHO================================================================================
#ECHO OFF
C:\Applications\Job\Job.exe > C:\Applications\Job\Job_Output\"Output_%date:/=-% %time::=-%.txt"
Try this (note the collated dot after echo):
echo.| C:\Applications\Job\Job.exe > C:\Applications\Job\Job_Output\"Output_%date:/=-% %time::=-%.txt"
I have tried with pause and it works well:
echo.| pause
echo. is not echo. It just prints a newline, just what you need to trigger the pause.
Not sure whether will it work if your console app already have a Console.ReadLine() or Console.ReadKey() method but instead of just calling the *.exe use the Start command which will run the executable in a separate window like
start "MyConsoleTask" C:\Applications\Job\Job.exe > C:\Applications\Job\Job_Output\"Output_%date:/=-% %time::=-%.txt"
If you have not access to the console app source code, you may try a workaround
#echo off
#echo================================================================================
#echo The Application is currently running and may take some time. Please wait...
#echo================================================================================
set "timeStamp=%date:/=-%_%time::=-%
set "timeStamp=%timeStamp:~0,-3%" & rem remove ,centiseconds.
set "logFile=C:\Applications\Job\Job_Output\Output_%timeStamp%.txt"
rem start the exe in the same cmd window
start /B "" """C:\Applications\Job\Job.exe" > "%logFile%"""
rem wait for process startup
ping 1.1.1.1 -n 1 -w 750 >NUL
rem wait for logFile to be closed. This may flag that job.exe has ended
:wait
ping 1.1.1.1 -n 1 -w 50 >NUL & rem this avoids processor load
2>nul (>>"%logFile%" call )||goto :wait
rem send a key to the console. This may be captured by the exe file
set "_vbs_file_=%TEMP%\sendConsole.vbs"
(
echo/ set oWS ^= CreateObject^("wScript.Shell"^)
echo/ wScript.Sleep 50
echo/ oWS.SendKeys "{ENTER}"
)>"%_vbs_file_%"
if exist "%TEMP%\sendConsole.vbs" (set "_spawn_=%TEMP%\sendConsole.vbs") else (set "_spawn_=sendConsole.vbs")
ping 1.1.1.1 -n 1 -w 50 >NUL
start /B /WAIT cmd /C "cls & "%_spawn_%" & del /F /Q "%_spawn_%" 2>NUL"
#echo================================================================================
#echo Process completed. I guess...
#echo================================================================================
exit/B
so,
start /B ...
starts the job.exe executable in the same cmd window.
:wait
ping 1.1.1.1 -n 1 -w 50 >NUL & rem this avoids processor load
2>nul (>>"%logFile%" call )||goto :wait
waits until logfile is closed, so it may indicate that the previous proccess has ended.
set "_vbs_file_=%TEMP%\sendConsole.vbs"
(
echo/ set oWS ^= CreateObject^("wScript.Shell"^)
echo/ wScript.Sleep 50
echo/ oWS.SendKeys "{ENTER}"
)>"%_vbs_file_%"
if exist "%TEMP%\sendConsole.vbs" (set "_spawn_=%TEMP%\sendConsole.vbs") else (set "_spawn_=sendConsole.vbs")
ping 1.1.1.1 -n 1 -w 50 >NUL
start /B /WAIT cmd /C "cls & "%_spawn_%" & del /F /Q "%_spawn_%" 2>NUL"
send the enter key to the console, so the process waiting a keystroke may capture it.
NOTE: the ping wait trick works fine only if the IP is unreachable.
NOTE: the call and/or goto trick is discussed here
we gotta simulate a key press here, therefore we should toy with the keyboard buffer.
I am no Batch expert and this is the answer I found searching how to press keys with a batch:
#if (#CodeSection == #Batch) #then
#echo off
set SendKeys=CScript //nologo //E:JScript "%~F0"
rem Open the command here
start "" /B Job.exe > JobOutput.txt
rem sends the keys composing the string "I PRESSED " and the enter key
%SendKeys% "I PRESSED {ENTER}"
goto :EOF
#end
// JScript section
WScript.CreateObject("WScript.Shell").SendKeys(WScript.Arguments(0));
source:
Press Keyboard keys using a batch file
GnuWin32 openssl s_client conn to WebSphere MQ server not closing at EOF, hangs

Command Line - Wait for a Process to Finish

I'm installing a set of drivers in an unattended script. One of the drivers (Intel USB3 Drivers) kicks off the Windows Driver Finder application ("drvinst.exe") after it's finished. Then, when the nVidia Drivers try to run, they cancel out because that Wizard is still running in the background.
My current solution is this, but it is not very elegant:
:INSTALLLAPTOP79
.\ELAN\Touchpad\Setup.exe /s /a /s
.\Intel\Chipset\Setup.exe -s -norestart
.\Intel\Graphics\Setup.exe -s
.\Intel\MEI\Setup.exe -s
.\Intel\USB3\Setup.exe -s
.\Realtek\Audio\Setup.exe /s
.\Realtek\CardReader\Setup.exe /s
TIMEOUT 180
.\nVidia\Graphics\Setup.exe -n -s
GOTO :INSTALLLAPTOPWIFI
Basically if a system is slower than "normal" it will fail as the 180 seconds isn't enough. I could just increase this value but that is messy to me.
I'm basically looking for a way to do a "check" to see if the "drvinst.exe" is still running and if so wait for a set period - then do the check again.
Any ideas?
It's not guaranteed to work (it depends on how the installers launch the driver-finder) but:
start /wait command...
may do the trick. Be aware that if the command to be executed contains spaces, and needs to be wrapped in double-quotes, you'll need:
start /wait "" "c:\program files\something\..."
otherwise it will take the command as the title of the command-prompt.
#ECHO OFF
SETLOCAL
notepad
:waitloop
TASKLIST /fi "imagename eq notepad.exe" >NUL
IF ERRORLEVEL 1 timeout /t 1 /n&GOTO waitloop
GOTO :EOF
Here's a simple method waiting for notepad.exe to close. Adapt as you will...
#ECHO OFF
SETLOCAL
notepad
:waitloop
TASKLIST |find "notepad.exe" >NUL
IF ERRORLEVEL 1 timeout /t 1 /n&GOTO waitloop
GOTO :EOF
should work also
This works for me in Windows 10 (1903). Caveat: if you use it for a process that is running multiple times, it waits for all of them to finish before continuing.
Based on this answer by #Magoo which didn't work for me, but put me on the right track.
#ECHO OFF
notepad.exe
REM For the OP's original problem, put drvinst.exe instead of notepad.exe (here and below)
:waitloop
TASKLIST |find /I "notepad.exe" >NUL
IF ERRORLEVEL 1 GOTO endloop
REM echo Notepad running. Waiting 1 second...
timeout /t 1 /nobreak>NUL
goto waitloop
:endloop
echo Done!
i dont know if this is the best way to do it, but i know it works 100%
I use call in my scripts when i need it to wait.
:INSTALLLAPTOP79
Call \ELAN\Touchpad\Setup.exe /s /a /s
Call \Intel\Chipset\Setup.exe -s -norestart
Call \Intel\Graphics\Setup.exe -s
Call \Intel\MEI\Setup.exe -s
Call \Intel\USB3\Setup.exe -s
Call \Realtek\Audio\Setup.exe /s
Call \Realtek\CardReader\Setup.exe /s
Call \nVidia\Graphics\Setup.exe -n -s
GOTO :INSTALLLAPTOPWIFI
Call will make the script wait until the exe that's being ran is finished.
Not sure what purpose the . before the \ serves but you may need it if its there for a reason.

Resources