Não apenas isso pode ser feito, mas apenas com um arquivo em lotes! :-)
O problema pode ser resolvido usando um arquivo temporário como um "pipe". A comunicação bidirecional requer dois arquivos "pipe".
Processo A lê stdin de "pipe1" e grava stdout em "pipe2"
Processo B lê stdin de "pipe2" e grava stdout em "pipe1"
É importante que os dois arquivos existam antes do início de qualquer processo. Os arquivos devem estar vazios no início.
Se um arquivo em lotes tentar ler um arquivo que está no final atual, ele simplesmente não retornará nada e o arquivo permanecerá aberto. Portanto, minha rotina readLine lê continuamente até obter um valor não vazio.
Quero poder ler e escrever uma sequência vazia, portanto, minha rotina writeLine acrescenta um caractere extra que a readLine retira.
Meu processo A controla o fluxo. Ele inicia as coisas escrevendo 1 (mensagem para B) e entra em um loop com 10 iterações, onde lê um valor (mensagem de B), adiciona 1 e depois grava o resultado (mensagem para B). Por fim, aguarda a última mensagem de B e, em seguida, grava uma mensagem "encerrar" em B e sai.
Meu processo B está em um loop condicionalmente interminável que lê um valor (mensagem de A), adiciona 10 e depois grava o resultado (mensagem em A). Se B alguma vez ler uma mensagem "encerrar", ela será encerrada imediatamente.
Queria demonstrar que a comunicação é totalmente síncrona, por isso introduzo um atraso nos loops do processo A e B.
Observe que o procedimento readLine está em um loop restrito que abusa continuamente a CPU e o sistema de arquivos enquanto aguarda a entrada. Um atraso PING pode ser adicionado ao loop, mas os processos não serão tão responsivos.
Eu uso um verdadeiro pipe como uma conveniência para iniciar os processos A e B. Mas o tubo não é funcional, pois nenhuma comunicação passa por ele. Toda a comunicação é feita através dos meus arquivos "pipe" temporários.
Eu poderia ter usado o START / B para iniciar os processos, mas preciso detectar quando os dois terminam, para saber quando excluir os arquivos "pipe" temporários. É muito mais simples usar o tubo.
Eu escolhi colocar todo o código em um único arquivo - o script mestre que inicia A e B, bem como o código para A e B. Eu poderia ter usado um arquivo de script separado para cada processo.
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
--RESULTADO--
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
A vida é um pouco mais fácil com uma linguagem de nível superior. Abaixo está um exemplo que usa VBScript para os processos A e B. Eu ainda uso o lote para iniciar os processos. Eu uso um método muito interessante descrito em É possível incorporar e executar o VBScript em um arquivo em lotes sem usar um arquivo temporário? para incorporar vários scripts VBS em um único script em lote.
Com um idioma mais alto como o VBS, podemos usar um canal normal para passar informações de A para B. Precisamos apenas de um único arquivo "canal" temporário para passar informações de B de volta para A. Como agora temos um canal em funcionamento, o A O processo não precisa enviar uma mensagem "encerrar" para B. O processo B simplesmente faz um loop até atingir o final do arquivo.
É bom ter acesso a uma função de suspensão adequada no VBS. Isso me permite introduzir facilmente um pequeno atraso na função readLine para dar uma pausa na CPU.
No entanto, há uma ruga no readLIne. No começo, eu estava recebendo falhas intermitentes até perceber que, às vezes, o readLine detectava informações disponíveis no stdin e tentava imediatamente ler a linha antes que B tivesse a chance de terminar de escrever a linha. Resolvi o problema introduzindo um pequeno atraso entre o teste de fim de arquivo e a leitura. Um atraso de 5 ms parecia fazer o truque para mim, mas eu dobrei para 10 ms apenas por segurança. É muito interessante que o lote não sofra esse problema. Discutimos isso brevemente (5 postagens curtas) em 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>
A saída é a mesma da solução em lote puro, exceto que as linhas finais "encerradas" não estão lá.