Как создать несколько процессов из оболочки Windows и дождаться их завершения? - PullRequest
13 голосов
/ 03 февраля 2010

Я хочу сделать следующее из пакетного скрипта Windows:

start proc1.exe

start proc2.exe

...

start procN.exe

<wait for all N processes to complete> <-- What do I put here?

Как мне дождаться завершения всех порожденных процессов?

Ответы [ 4 ]

12 голосов
/ 03 февраля 2010

Это ужасно, но вы можете заключить каждую команду в другой пакетный файл, который запускает команду и затем сигнализирует, что команда выполнена. Ваш основной пакетный файл будет вызывать каждый из этих пакетных файлов асинхронно и сидеть в цикле в ожидании сигналов.

Например:

main.bat

start cmd /c proc1.bat
start cmd /c proc2.bat
:wait
sleep 1
IF NOT EXIST proc1done GOTO wait
IF NOT EXIST proc2done GOTO wait
del proc1done
del proc2done
echo All processes are complete

proc1.bat

proc1.exe
echo Done > proc1done

Команда сна доступна в Средства набора ресурсов для Windows Server 2003 . Если у вас этого нет, вы можете использовать ping на localhost, чтобы замедлить этот жесткий цикл.

1 голос
/ 18 марта 2019

Я нашел хорошее решение в использовании альтернативной оболочки под названием "Йори". Вы можете прочитать больше об этом и загрузить его с http://www.malsmith.net/yori/. Yori с открытым исходным кодом, а его хранилище кода находится на https://github.com/malxau/yori.

Yori облегчает реализацию параллельного выполнения процессов и ожидает их завершения. По сути, вам нужно запустить различные подпроцессы как так называемые «задания», а затем использовать внутреннюю команду «ждать», чтобы дождаться их завершения. Вы можете запустить любой процесс как задание, добавив специальные символы "&!" в конце команды. Как то так:

ping localhost &!
ping someotherhost &!
wait

Вы можете поместить свои зависимые от Yori команды в отдельный пакетный файл (с расширением Yori-пакета ".ys1") и вызвать его из своего Windows-пакета, например, с помощью следующей команды:

echo Let Yori do the parallel execution of some lengthy operations...
start /wait /b /belownormal yori.exe -c "your-yori-script.ys1"
echo All the lengthy operations are finished, continue with the next steps...
0 голосов
/ 16 сентября 2013

Как правило, вы делаете что-то подобное с использованием Python, но здесь это происходит (вдохновленный этим постом и другими):

calcFromList.bat < DesiredBatchFile > < TxtFile > [--async [SleepTimeBetweenSpawns]]

Он будет порождать один процесс для каждой строки в TxtFile (строка используется для указания отдельных параметров для пакетного файла).

calcFromList.bat:

echo off

pushd \
%~d0
cd "%~dp0"

set CALL_BAT="%~1"
set FILE_CONTAINING_LIST="%~2"

if %CALL_BAT%=="" (goto badval)
if %FILE_CONTAINING_LIST%=="" (goto badval2)

if "%3"=="--async" goto async

for /f "delims=¬" %%X in ('type %FILE_CONTAINING_LIST%') do (call %CALL_BAT% %%X)
goto end

:async
set /a AsyncCountNum=1

set SLEEP_VALUE_S=0

if not "%4"=="" (set SLEEP_VALUE_S=%4)
set AsyncParamBat=%CALL_BAT%

:: Find unique id even if called simultaneously (using simple collision detection)
:newid
timeout /t 1 /nobreak > NUL
set AsyncRand=%RANDOM:~-1%%RANDOM:~-2%%RANDOM:~-3%
set AsyncCountFilePrefix="%FILE_CONTAINING_LIST:"=%__%AsyncRand%"
set AsyncCollisionText="%~1;%~2;%~3;%~4;"
IF EXIST "%AsyncCountFilePrefix:"=%0" GOTO newid
echo %AsyncCollisionText% > "%AsyncCountFilePrefix:"=%_init_async_done.txt"
set /p AsyncCheckColision=<"%AsyncCountFilePrefix:"=%_init_async_done.txt"

if "%AsyncCollisionText:"=% "=="%AsyncCheckColision:"=%" GOTO idfound
timeout /t 1 /nobreak > NUL
GOTO newid
:idfound

echo Beginning spawning of processes (id = %AsyncRand%)...

for /f "delims=¬" %%X in ('type %FILE_CONTAINING_LIST%') do (set AsyncParamCall="%%~X" & start "Batch (from id %AsyncRand%)" /D "%~dp0" "cmd /c asyncExec.bat" & set /a AsyncCountNum+=1 & timeout /t %SLEEP_VALUE_S% /nobreak > NUL)

set /a AsyncCountNum-=1
echo All %AsyncCountNum% processes spawned, waiting for their completion...
:wait
timeout /t 2 /nobreak > NUL
for /l %%x in (1, 1, %AsyncCountNum%) do (IF NOT EXIST "%AsyncCountFilePrefix:"=%_%%x_tmp_async_done.txt" GOTO wait )

echo Finished, cleaning up...
for /l %%x in (1, 1, %AsyncCountNum%) do (del /F /Q  "%AsyncCountFilePrefix:"=%_%%x_tmp_async_done.txt")
del /F /Q "%AsyncCountFilePrefix:"=%_init_async_done.txt"
goto end

start "Calib for %~TARGET_FOLDER%" /D "%~dp0" "cmd /k calibCamsSequence.bat"

goto end
:badval
echo No bat file specified! (first parameter)

goto end
:badval2
echo No list file specified! (second parameter)

:end
popd

asyncExec.bat:

@call %AsyncParamBat% %AsyncParamCall%
@echo "Done at %DATE% %TIME%" > "%AsyncCountFilePrefix:"=%_%AsyncCountNum%_tmp_async_done.txt"
0 голосов
/ 03 февраля 2010

Вы могли бы сделать это, если вы пишете код .NET или код Win32 C / C ++ для запуска процессов, но в пакетном файле нет способа сделать это. Как только вы используете start, чтобы сделать proc1.exe асинхронным, нет пакетной команды, которая позволила бы вам вернуться позже и дождаться ее завершения.

Но вы можете легко сделать это на одном из языков сценариев, предназначенных для пакетной работы, Python, WSH и т. Д.

Например, вот простой скрипт с использованием Windows Script Host . WSH включен во все версии Windows, начиная с Windows 98 . Так что этот скрипт должен работать где угодно.

Этот скрипт запускает две программы и ожидает их завершения. Сохраните его как start2.wsf. Затем просто используйте его в вашем командном файле:

start2.wsf "prog1.exe" "prog2.exe"


<package>
  <job id="Start 2 Procs">
    <runtime>
      <description>Start 2 programs and wait for them both to finish.
      </description>
      <unnamed
        name="program"
        helpstring="the program to run"
        many="false"
        required="2"
      />
      <example>
        Example: Start2.wsf "cl -c foo.obj" "cl -c bar.obj"
      </example>
    </runtime>

    <script language="JScript">

      if (WScript.Arguments.length < 2)
      {
        WScript.Arguments.ShowUsage();
        WScript.Quit();
      }

      var proc1 = WScript.Arguments.Unnamed.Item(0);
      var proc2 = WScript.Arguments.Unnamed.Item(1);

      var Shell = WScript.CreateObject("WScript.Shell");
      var oProc1 = Shell.Exec(proc1);
      var oProc2 = Shell.Exec(proc2);

      while (oProc1.Status == 0 && oProc2.Status == 0)
      {
        WScript.Sleep(1000);
      }
    </script>
  </job>
</package>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...