Non seulement cela peut être fait, mais cela ne peut être fait qu'avec un fichier batch! :-)
Le problème peut être résolu en utilisant un fichier temporaire comme "pipe". La communication bidirectionnelle nécessite deux fichiers "pipe".
Le processus A lit stdin de "pipe1" et écrit stdout dans "pipe2" Le
processus B lit stdin de "pipe2" et écrit stdout dans "pipe1"
Il est important que les deux fichiers existent avant de lancer l'un ou l'autre processus. Les fichiers doivent être vides au début.
Si un fichier de commandes tente de lire à partir d'un fichier qui se trouve être à la fin actuelle, il ne renvoie simplement rien et le fichier reste ouvert. Ainsi, ma routine readLine lit en continu jusqu'à ce qu'elle obtienne une valeur non vide.
Je veux pouvoir lire et écrire une chaîne vide, donc ma routine writeLine ajoute un caractère supplémentaire que readLine supprime.
Mon processus A contrôle le flux. Il initie les choses en écrivant 1 (message à B), puis entre dans une boucle avec 10 itérations où il lit une valeur (message de B), ajoute 1, puis écrit le résultat (message à B). Enfin, il attend le dernier message de B, puis écrit un message "quitter" dans B et quitte.
Mon processus B se trouve dans une boucle conditionnellement sans fin qui lit une valeur (message de A), ajoute 10, puis écrit le résultat (message dans A). Si B lit jamais un message "quitter", il se termine immédiatement.
Je voulais démontrer que la communication est entièrement synchrone, donc j'introduis un retard dans les boucles de processus A et B.
Notez que la procédure readLine est dans une boucle étroite qui abuse continuellement à la fois du CPU et du système de fichiers pendant qu'elle attend l'entrée. Un délai PING pourrait être ajouté à la boucle, mais les processus ne seront pas aussi réactifs.
J'utilise un vrai tuyau pour faciliter le lancement des processus A et B. Mais le tuyau n'est pas fonctionnel en ce sens qu'aucune communication ne le traverse. Toutes les communications se font via mes fichiers "pipe" temporaires.
J'aurais tout aussi bien pu utiliser START / B pour lancer les processus, mais je dois ensuite détecter quand ils se terminent tous les deux afin de savoir quand supprimer les fichiers temporaires "pipe". Il est beaucoup plus simple d'utiliser le tuyau.
J'ai choisi de mettre tout le code dans un seul fichier - le script principal qui lance A et B, ainsi que le code pour A et B. J'aurais pu utiliser un fichier de script distinct pour chaque processus.
test.bat
@echo off
if "%~1" equ "" (
copy nul pipe1.txt >nul
copy nul pipe2.txt >nul
"%~f0" A <pipe1.txt >>pipe2.txt | "%~f0" B <pipe2.txt >>pipe1.txt
del pipe1.txt pipe2.txt
exit /b
)
setlocal enableDelayedExpansion
set "prog=%~1"
goto !prog!
:A
call :writeLine 1
for /l %%N in (1 1 5) do (
call :readLine
set /a ln+=1
call :delay 1
call :writeLine !ln!
)
call :readLine
call :delay 1
call :writeLine quit
exit /b
:B
call :readLine
if !ln! equ quit exit /b
call :delay 1
set /a ln+=10
call :writeLine !ln!
goto :B
:readLine
set "ln="
set /p "ln="
if not defined ln goto :readLine
set "ln=!ln:~0,-1!"
>&2 echo !prog! reads !ln!
exit /b
:writeLine
>&2 echo !prog! writes %*
echo(%*.
exit /b
:delay
setlocal
set /a cnt=%1+1
ping localhost /n %cnt% >nul
exit /b
--PRODUCTION--
C:\test>test
A writes 1
B reads 1
B writes 11
A reads 11
A writes 12
B reads 12
B writes 22
A reads 22
A writes 23
B reads 23
B writes 33
A reads 33
A writes 34
B reads 34
B writes 44
A reads 44
A writes 45
B reads 45
B writes 55
A reads 55
A writes 56
B reads 56
B writes 66
A reads 66
A writes quit
B reads quit
La vie est un peu plus facile avec une langue de niveau supérieur. Voici un exemple qui utilise VBScript pour les processus A et B. J'utilise toujours le batch pour lancer les processus. J'utilise une méthode très cool décrite dans Est-il possible d'incorporer et d'exécuter VBScript dans un fichier batch sans utiliser de fichier temporaire? pour incorporer plusieurs scripts VBS dans un même script batch.
Avec un langage plus élevé comme VBS, nous pouvons utiliser un canal normal pour transmettre des informations de A à B. Nous n'avons besoin que d'un seul fichier "canal" temporaire pour transmettre des informations de B à A. Parce que nous avons maintenant un canal fonctionnel, le A le processus n'a pas besoin d'envoyer un message "quitter" à B. Le processus B boucle simplement jusqu'à ce qu'il atteigne la fin du fichier.
C'est bien d'avoir accès à une fonction de veille appropriée dans VBS. Cela me permet d'introduire facilement un court délai dans la fonction readLine pour donner une pause au CPU.
Cependant, il y a une ride dans readLIne. Au début, j'obtenais des échecs intermittents jusqu'à ce que je réalise que parfois readLine détectait que des informations étaient disponibles sur stdin et essayait immédiatement de lire la ligne avant que B n'ait la chance de terminer l'écriture de la ligne. J'ai résolu le problème en introduisant un court délai entre le test de fin de fichier et la lecture. Un retard de 5 ms semblait faire l'affaire pour moi, mais je l'ai doublé à 10 ms juste pour être sûr. Il est très intéressant que le lot ne souffre pas de ce problème. Nous en avons discuté brièvement (5 courts messages) sur http://www.dostips.com/forum/viewtopic.php?f=3&t=7078#p47432 .
<!-- : Begin batch script
@echo off
copy nul pipe.txt >nul
cscript //nologo "%~f0?.wsf" //job:A <pipe.txt | cscript //nologo "%~f0?.wsf" //job:B >>pipe.txt
del pipe.txt
exit /b
----- Begin wsf script --->
<package>
<job id="A"><script language="VBS">
dim ln, n, i
writeLine 1
for i=1 to 5
ln = readLine
WScript.Sleep 1000
writeLine CInt(ln)+1
next
ln = readLine
function readLine
do
if not WScript.stdin.AtEndOfStream then
WScript.Sleep 10 ' Pause a bit to let B finish writing the line
readLine = WScript.stdin.ReadLine
WScript.stderr.WriteLine "A reads " & readLine
exit function
end if
WScript.Sleep 10 ' This pause is to give the CPU a break
loop
end function
sub writeLine( msg )
WScript.stderr.WriteLine "A writes " & msg
WScript.stdout.WriteLine msg
end sub
</script></job>
<job id="B"> <script language="VBS">
dim ln, n
do while not WScript.stdin.AtEndOfStream
ln = WScript.stdin.ReadLine
WScript.stderr.WriteLine "B reads " & ln
n = CInt(ln)+10
WScript.Sleep 1000
WScript.stderr.WriteLine "B writes " & n
WScript.stdout.WriteLine n
loop
</script></job>
</package>
La sortie est la même que pour la solution par lots pure, sauf que les lignes finales de «sortie» ne sont pas là.