web-dev-qa-db-fra.com

Exécution parallèle des processus Shell

Existe-t-il un outil pour exécuter plusieurs processus en parallèle dans un fichier batch Windows? J'ai trouvé des outils intéressants pour Linux ( parallèle et PPSS ), cependant, j'aurais besoin d'un outil pour les plates-formes Windows.

Bonus: Ce serait formidable si l'outil permettait également de distribuer facilement les processus entre plusieurs machines, en exécutant les processus à distance à la PsExec .

Exemple: je voudrais que dans la boucle for suivante

for %F in (*.*) do processFile.exe %F

un nombre limité d'instances de processFile.exe s'exécutent en parallèle pour tirer parti des processeurs multicœurs.

43
Dirk Vollmar

GNU xargs sous Linux a un commutateur "-P n" pour lancer les processus "n" en parallèle.

Peut-être que la construction cygwin/mingw de xargs prend également en charge cela?

Ensuite, vous pouvez utiliser:

xargs -P 4 processFile < fileList

Cependant, aucun processus multi-nœuds sophistiqué n'apparaît.

5
ADEpt

Modifier - J'ai modifié le script pour afficher éventuellement la sortie de chaque processus

Voici une solution native par lots qui exécute de manière fiable une liste de commandes en parallèle, ne lançant jamais plus de n processus à la fois .

Il a même un mécanisme intégré pour distribuer les processus à des CPU spécifiques ou à des machines distantes via PSEXEC, mais je n'ai pas testé cette fonctionnalité.

L'astuce pour que cela fonctionne est de démarrer chaque commande via un processus CMD qui redirige stdout ou un handle non défini vers un fichier de verrouillage. Le processus maintiendra un verrou exclusif sur le fichier jusqu'à ce qu'il se termine. Peu importe la façon dont le processus se termine (sortie normale, crash, processus tué), le verrou sera libéré dès qu'il le fera.

Le script maître peut tester si le processus est toujours actif en tentant de rediriger vers le même fichier de verrouillage. La redirection échouera si le processus est toujours actif, réussira s'il s'est terminé.

Par défaut, le script ignore la sortie de chaque processus. Si commencé avec le /O comme premier paramètre, puis il affiche la sortie de chaque processus, sans entrelacement.

Ma démo définit la limite de processus à 4 et exécute simplement une série de commandes PING de longueur variable.

J'ai testé cela sur XP, Vista et Windows 7.

@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="
)

:: The list of commands could come from anywhere such as another file
:: or the output of another command. For this demo I will list the
:: commands within this script - Each command is prefixed with :::
::: ping /n 05 ::1
::: ping /n 20 ::1
::: ping /n 10 ::1
::: ping /n 15 ::1
::: ping /n 07 ::1
::: ping /n 05 ::1
::: ping /n 20 ::1
::: ping /n 10 ::1
::: ping /n 15 ::1
::: ping /n 07 ::1

:: Define the maximum number of parallel processes to run.
:: Each process number can optionally be assigned to a particular server
:: and/or cpu via psexec specs (untested).
set "maxProc=4"

:: Optional - Define CPU targets in terms of PSEXEC specs
::           (everything but the command)
::
:: If a CPU is not defined for a proc, then it will be run on the local machine.
:: I haven't tested this feature, but it seems like it should work.
::
:: set cpu1=psexec \\server1 ...
:: set cpu2=psexec \\server1 ...
:: set cpu3=psexec \\server2 ...
:: etc.

:: For this demo force all CPU specs to undefined (local machine)
for /l %%N in (1 1 %maxProc%) do set "cpu%%N="

:: 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
:: Modify the IN () clause as needed to retrieve the list of commands
  set launch=1
  for /f "tokens=* delims=:" %%A in ('findstr /b ":::" "%~f0"') 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 !cpu%%N! %%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 Thats all folks^^!

Voici la sortie d'un exemple d'exécution qui ignore la sortie du processus

12:24:07.52 - proc1: starting  ping /n 05 ::1
12:24:07.52 - proc2: starting  ping /n 20 ::1
12:24:07.53 - proc3: starting  ping /n 10 ::1
12:24:07.54 - proc4: starting  ping /n 15 ::1
12:24:11.60 - proc1: finished  ping /n 05 ::1
12:24:11.60 - proc1: starting  ping /n 07 ::1
12:24:16.66 - proc3: finished  ping /n 10 ::1
12:24:16.66 - proc3: starting  ping /n 05 ::1
12:24:17.68 - proc1: finished  ping /n 07 ::1
12:24:17.68 - proc1: starting  ping /n 20 ::1
12:24:20.72 - proc3: finished  ping /n 05 ::1
12:24:20.72 - proc3: starting  ping /n 10 ::1
12:24:21.75 - proc4: finished  ping /n 15 ::1
12:24:21.75 - proc4: starting  ping /n 15 ::1
12:24:26.82 - proc2: finished  ping /n 20 ::1
12:24:26.82 - proc2: starting  ping /n 07 ::1
12:24:29.86 - proc3: finished  ping /n 10 ::1
12:24:32.89 - proc2: finished  ping /n 07 ::1
12:24:35.92 - proc4: finished  ping /n 15 ::1
12:24:36.93 - proc1: finished  ping /n 20 ::1
Thats all folks!

Voici la sortie si exécutée avec le /O option affichant la sortie du processus

-------------------------------------------------------------------------------
12:24:51.02 - proc1: starting  ping /n 05 ::1
-------------------------------------------------------------------------------
12:24:51.02 - proc2: starting  ping /n 20 ::1
-------------------------------------------------------------------------------
12:24:51.03 - proc3: starting  ping /n 10 ::1
-------------------------------------------------------------------------------
12:24:51.04 - proc4: starting  ping /n 15 ::1
===============================================================================
12:24:55.10 - proc1: finished  ping /n 05 ::1

Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms

Ping statistics for ::1:
    Packets: Sent = 5, Received = 5, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
-------------------------------------------------------------------------------
12:24:55.10 - proc1: starting  ping /n 07 ::1
===============================================================================
12:25:00.17 - proc3: finished  ping /n 10 ::1

Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms

Ping statistics for ::1:
    Packets: Sent = 10, Received = 10, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
-------------------------------------------------------------------------------
12:25:00.19 - proc3: starting  ping /n 05 ::1
===============================================================================
12:25:01.22 - proc1: finished  ping /n 07 ::1

Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms

Ping statistics for ::1:
    Packets: Sent = 7, Received = 7, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
-------------------------------------------------------------------------------
12:25:01.23 - proc1: starting  ping /n 20 ::1
===============================================================================
12:25:04.27 - proc3: finished  ping /n 05 ::1

Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms

Ping statistics for ::1:
    Packets: Sent = 5, Received = 5, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
-------------------------------------------------------------------------------
12:25:04.28 - proc3: starting  ping /n 10 ::1
===============================================================================
12:25:05.30 - proc4: finished  ping /n 15 ::1

Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms

Ping statistics for ::1:
    Packets: Sent = 15, Received = 15, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
-------------------------------------------------------------------------------
12:25:05.32 - proc4: starting  ping /n 15 ::1
===============================================================================
12:25:10.38 - proc2: finished  ping /n 20 ::1

Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms

Ping statistics for ::1:
    Packets: Sent = 20, Received = 20, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
-------------------------------------------------------------------------------
12:25:10.40 - proc2: starting  ping /n 07 ::1
===============================================================================
12:25:13.44 - proc3: finished  ping /n 10 ::1

Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms

Ping statistics for ::1:
    Packets: Sent = 10, Received = 10, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
===============================================================================
12:25:16.48 - proc2: finished  ping /n 07 ::1

Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms

Ping statistics for ::1:
    Packets: Sent = 7, Received = 7, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
===============================================================================
12:25:19.52 - proc4: finished  ping /n 15 ::1

Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms

Ping statistics for ::1:
    Packets: Sent = 15, Received = 15, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
===============================================================================
12:25:20.54 - proc1: finished  ping /n 20 ::1

Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms

Ping statistics for ::1:
    Packets: Sent = 20, Received = 20, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
===============================================================================
Thats all folks!
51
dbenham

Essayez start:

start "title of the process" "P:\ath\to.exe"

Il ouvre une nouvelle fenêtre avec le titre donné et exécute le fichier BAT, CMD ou EXE. Vous pouvez également définir la priorité, définir le même environnement, etc.

Les fichiers non exécutables sont ouverts avec le programme associé.

Pour en savoir plus: Démarrer -> Exécuter

cmd /k start /?

Start est disponible au moins depuis WinME.

Bonne chance!

23
guerda

Cela ressemble plus à ce que vous souhaitez utiliser Powershell 2. Cependant, vous pouvez générer de nouvelles fenêtres cmd (ou d'autres processus) en utilisant start, voir aussi this answer. Bien que vous deviez probablement utiliser d'autres outils et un peu de ruse pour créer quelque chose comme un "pool de processus" (pour n'avoir qu'un maximum de n instances en cours d'exécution à la fois). Vous pouvez atteindre ce dernier en utilisant tasklist /im et en comptant combien sont déjà là (for boucle ou wc, le cas échéant) et attendez simplement (ping -n 2 ::1 >nul 2>&1) et vérifiez à nouveau si vous pouvez générer un nouveau processus.

J'ai bricolé un petit lot de test pour cela:

@echo off
for /l %%i in (1,1,20) do call :loop %%i
goto :eof

:loop
call :checkinstances
if %INSTANCES% LSS 5 (
    rem just a dummy program that waits instead of doing useful stuff
    rem but suffices for now
    echo Starting processing instance for %1
    start /min wait.exe 5 sec
    goto :eof
)
rem wait a second, can be adjusted with -w (-n 2 because the first ping returns immediately;
rem otherwise just use an address that's unused and -n 1)
echo Waiting for instances to close ...
ping -n 2 ::1 >nul 2>&1
rem jump back to see whether we can spawn a new process now
goto loop
goto :eof

:checkinstances
rem this could probably be done better. But INSTANCES should contain the number of running instances afterwards.
for /f "usebackq" %%t in (`tasklist /fo csv /fi "imagename eq wait.exe"^|find /c /v ""`) do set INSTANCES=%%t
goto :eof

Il génère un maximum de quatre nouveaux processus qui s'exécutent en parallèle et sont minimisés. Le temps d'attente doit être ajusté probablement, en fonction de la durée de chaque processus et de sa durée. Vous devrez probablement également ajuster le nom du processus pour lequel la liste des tâches recherche si vous faites autre chose.

Il n'y a cependant aucun moyen de compter correctement les processus générés par ce lot. Une façon serait de créer un nombre aléatoire au début du lot (%RANDOM%) et créer un lot d'assistance qui effectue le traitement (ou génère le programme de traitement) mais qui peut définir son titre de fenêtre sur un paramètre:

@echo off
title %1
"%2" "%3"

Ce serait un lot simple qui définit son titre sur le premier paramètre, puis exécute le deuxième paramètre avec le troisième comme argument. Vous pouvez ensuite filtrer dans la liste des tâches en sélectionnant uniquement les processus avec le titre de fenêtre spécifié (tasklist /fi "windowtitle eq ..."). Cela devrait fonctionner assez fiable et éviter trop de faux positifs. À la recherche de cmd.exe serait une mauvaise idée si vous avez encore des instances en cours d'exécution, car cela limite votre pool de processus de travail.

Vous pouvez utiliser %NUMBER_OF_PROCESSORS% pour créer une valeur par défaut raisonnable du nombre d'instances à générer.

Vous pouvez également l'adapter facilement pour utiliser psexec pour lancer les processus à distance (mais ce ne serait pas très viable car vous devez avoir des privilèges d'administrateur sur l'autre machine et fournir le mot de passe dans le lot). Vous devrez alors utiliser des noms de processus pour le filtrage.

19
Joey

Il existe un clone de base de type xargs de Windows qui prend en charge l'option de traitement parallèle -P sur http://www.pirosa.co.uk/demo/wxargs/wxargs.html

6
PP.