Não só pode ser feito, como também pode ser feito com um arquivo de lote! : -)
O problema pode ser resolvido usando um arquivo temporário como um "pipe". A comunicação bidirecional requer dois arquivos "pipe".
O processo A lê stdin de "pipe1" e grava stdout em "pipe2"
O processo B lê stdin de "pipe2" e escreve stdout para "pipe1"
É importante que ambos os arquivos existam antes de iniciar qualquer um dos processos. Os arquivos devem estar vazios no início.
Se um arquivo em lote tentar ler de um arquivo que esteja no final atual, ele simplesmente não retornará nada e o arquivo permanecerá aberto. Então, minha rotina readLine lê continuamente até obter um valor não vazio.
Eu quero ser capaz de ler e escrever uma string vazia, então minha rotina writeLine anexa um caractere extra que o readLine remove.
Meu processo controla o fluxo. Ele inicia as coisas escrevendo 1 (mensagem para B) e, em seguida, insere um loop com 10 iterações, onde lê um valor (mensagem de B), adiciona 1 e grava o resultado (mensagem para B). Finalmente, ele aguarda a última mensagem de B e, em seguida, escreve uma mensagem "sair" para B e sai.
Meu processo B está em um loop condicionalmente infinito que lê um valor (mensagem de A), adiciona 10 e grava o resultado (mensagem para A). Se B alguma vez ler uma mensagem "quit", ela terminará imediatamente.
Eu queria demonstrar que a comunicação é totalmente síncrona, então introduzo um atraso nos loops dos processos A e B.
Observe que o procedimento readLine está em um circuito fechado que continuamente abusa tanto a CPU quanto o sistema de arquivos enquanto aguarda a entrada. Um atraso de PING pode ser adicionado ao loop, mas os processos não serão tão responsivos.
Eu uso um pipe verdadeiro 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 muito bem ter usado o START / B para iniciar os processos, mas tenho que detectar quando ambos terminam para que eu saiba 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
- OUTPUT -
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 o VBScript para os processos A e B. Eu ainda uso lote para lançar os processos. Eu uso um método muito legal descrito em É possível incorporar e executar o VBScript dentro de um arquivo de lote sem usar um arquivo temporário? para incorporar vários scripts VBS em um único script em lote.
Com uma linguagem mais alta como o VBS, podemos usar um pipe normal para passar informações de A para B. Precisamos apenas de um único arquivo "pipe" temporário para passar informações de B de volta para A. Porque agora temos um canal funcionando , o processo A não precisa enviar uma mensagem "quit" para B. O processo B simplesmente faz um loop até chegar ao fim do arquivo.
Com certeza é legal 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 tendo 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. Eu resolvi o problema introduzindo um pequeno atraso entre o teste de fim de arquivo e a leitura. Um atraso de 5 msec pareceu fazer o truque para mim, mas eu dobrei para 10 ms apenas para estar no lado seguro. É muito interessante que o lote não sofra esse problema. Discutimos isso brevemente (cinco postagens curtas) no link .
<!-- : 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 que com a solução pura de lote, exceto que as linhas finais de "quit" não estão lá.