¡No solo se puede hacer, se puede hacer con nada más que un archivo por lotes! :-)
El problema se puede resolver mediante el uso de un archivo temporal como "tubería". La comunicación bidireccional requiere dos archivos "pipe".
El proceso A lee stdin de "pipe1" y escribe stdout en "pipe2" El
proceso B lee stdin de "pipe2" y escribe stdout en "pipe1"
Es importante que ambos archivos existan antes de iniciar cualquiera de los procesos. Los archivos deben estar vacíos al inicio.
Si un archivo por lotes intenta leer de un archivo que se encuentra en el extremo actual, simplemente no devuelve nada y el archivo permanece abierto. Entonces, mi rutina readLine se lee continuamente hasta que obtiene un valor no vacío.
Quiero poder leer y escribir una cadena vacía, por lo que mi rutina writeLine agrega un carácter adicional que readLine elimina.
Mi proceso A controla el flujo. Inicia cosas escribiendo 1 (mensaje a B), y luego ingresa a un ciclo con 10 iteraciones donde lee un valor (mensaje de B), agrega 1 y luego escribe el resultado (mensaje a B). Finalmente, espera el último mensaje de B, y luego escribe un mensaje de "salir" a B y sale.
Mi proceso B está en un bucle condicionalmente interminable que lee un valor (mensaje de A), agrega 10 y luego escribe el resultado (mensaje en A). Si B alguna vez lee un mensaje de "salir", entonces termina inmediatamente.
Quería demostrar que la comunicación es totalmente sincrónica, por lo que introduzco un retraso en los bucles de proceso A y B.
Tenga en cuenta que el procedimiento readLine está en un circuito cerrado que abusa continuamente de la CPU y del sistema de archivos mientras espera la entrada. Se podría agregar un retraso PING al bucle, pero luego los procesos no serán tan receptivos.
Utilizo una tubería verdadera como una conveniencia para iniciar los procesos A y B. Pero la tubería no funciona porque no pasa comunicación por ella. Toda la comunicación es a través de mis archivos temporales "pipe".
También podría haber usado START / B para iniciar los procesos, pero luego tengo que detectar cuándo ambos terminan para saber cuándo eliminar los archivos temporales "pipe". Es mucho más simple usar la tubería.
Elegí poner todo el código en un solo archivo: el script maestro que inicia A y B, así como el código para A y B. Podría haber usado un archivo de script separado para cada proceso.
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
--SALIDA--
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 vida es un poco más fácil con un lenguaje de nivel superior. A continuación se muestra un ejemplo que usa VBScript para los procesos A y B. Todavía uso el lote para iniciar los procesos. Utilizo un método muy bueno descrito en ¿Es posible incrustar y ejecutar VBScript dentro de un archivo por lotes sin usar un archivo temporal? para incrustar múltiples scripts de VBS dentro de un solo script por lotes.
Con un lenguaje superior como VBS, podemos usar una tubería normal para pasar información de A a B. Solo necesitamos un único archivo "tubería" temporal para pasar información de B de nuevo a A. Como ahora tenemos una tubería funcional, la A el proceso no necesita enviar un mensaje de "salir" a B. El proceso B simplemente se repite hasta que llega al final del archivo.
Seguro que es bueno tener acceso a una función de suspensión adecuada en VBS. Esto me permite introducir fácilmente un pequeño retraso en la función readLine para dar un descanso a la CPU.
Sin embargo, hay una arruga dentro de readLIne. Al principio estaba teniendo fallas intermitentes hasta que me di cuenta de que a veces readLine detectaría que la información está disponible en stdin, e inmediatamente trataría de leer la línea antes de que B tuviera la oportunidad de terminar de escribirla. Resolví el problema introduciendo un pequeño retraso entre la prueba de fin de archivo y la lectura. Un retraso de 5 ms pareció hacerme el truco, pero lo dupliqué a 10 ms para estar seguro. Es muy interesante que el lote no sufra este problema. Discutimos esto brevemente (5 publicaciones cortas) en 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 salida es la misma que con la solución de lote puro, excepto que las líneas finales de "dejar de fumar" no están allí.