Można to zrobić nie tylko, ale tylko za pomocą pliku wsadowego! :-)
Problem można rozwiązać, używając pliku tymczasowego jako „potoku”. Komunikacja dwukierunkowa wymaga dwóch plików „potokowych”.
Proces A odczytuje stdin z „potoku 1” i zapisuje stdout na „potoku 2”
Proces B odczytuje stdin z „potoku 2” i zapisuje stdout na „potoku 1”
Ważne jest, aby oba pliki istniały przed uruchomieniem któregokolwiek z procesów. Pliki powinny być puste na początku.
Jeśli plik wsadowy próbuje odczytać z pliku, który znajduje się na bieżącym końcu, po prostu nic nie zwraca, a plik pozostaje otwarty. Więc moja procedura readLine ciągle czyta, dopóki nie otrzyma niepustej wartości.
Chcę móc czytać i pisać pusty ciąg, więc moja procedura writeLine dołącza dodatkowy znak, który readLine usuwa.
Mój proces A kontroluje przepływ. Inicjuje rzeczy, pisząc 1 (wiadomość do B), a następnie wchodzi w pętlę z 10 iteracjami, w której odczytuje wartość (wiadomość z B), dodaje 1, a następnie zapisuje wynik (wiadomość do B). W końcu czeka na ostatnią wiadomość z B, a następnie zapisuje wiadomość „wyjdź” do B i kończy działanie.
Mój proces B znajduje się w warunkowo nieskończonej pętli, która odczytuje wartość (wiadomość z A), dodaje 10, a następnie zapisuje wynik (wiadomość do A). Jeśli B kiedykolwiek czyta komunikat „wyjdź”, to natychmiast się kończy.
Chciałem wykazać, że komunikacja jest w pełni synchroniczna, dlatego wprowadzam opóźnienie zarówno w pętlach procesowych A, jak i B.
Zauważ, że procedura readLine znajduje się w ciasnej pętli, która stale nadużywa zarówno procesora, jak i systemu plików, czekając na dane wejściowe. Do pętli można dodać opóźnienie PING, ale wówczas procesy nie będą tak responsywne.
Używam prawdziwej potoku jako wygody do uruchamiania zarówno procesów A, jak i B. Ale rura nie działa, ponieważ nie przechodzi przez nią komunikacja. Cała komunikacja odbywa się za pośrednictwem moich tymczasowych plików „potokowych”.
Równie dobrze mogłem użyć START / B do uruchomienia procesów, ale potem muszę wykryć, kiedy oba kończą się, aby wiedzieć, kiedy należy usunąć tymczasowe pliki „potoku”. Korzystanie z rury jest znacznie prostsze.
Zdecydowałem się umieścić cały kod w jednym pliku - skrypcie głównym, który uruchamia A i B, a także kod A i B. Mogłem użyć osobnego pliku skryptu dla każdego procesu.
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
--WYNIK--
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
Życie jest trochę łatwiejsze dzięki językowi wyższego poziomu. Poniżej znajduje się przykład, który używa VBScript dla procesów A i B. Nadal używam partii do uruchomienia procesów. Używam bardzo fajnej metody opisanej na stronie Czy można osadzić i uruchomić VBScript w pliku wsadowym bez użycia pliku tymczasowego? aby osadzić wiele skryptów VBS w jednym skrypcie wsadowym.
W języku wyższym, takim jak VBS, możemy użyć zwykłego potoku, aby przekazać informacje z A do B. Potrzebujemy tylko jednego tymczasowego pliku „potoku”, aby przekazać informacje z B z powrotem do A. Ponieważ mamy teraz działający potok, A proces nie musi wysyłać komunikatu „wyjdź” do B. Proces B po prostu zapętla się, aż osiągnie koniec pliku.
Na pewno miło jest mieć dostęp do odpowiedniej funkcji uśpienia w VBS. Pozwala mi to łatwo wprowadzić krótkie opóźnienie w funkcji readLine, aby przerwać procesor.
Jednak w jednym czytaniu jest jedna zmarszczka. Na początku pojawiały się sporadyczne awarie, dopóki nie zdałem sobie sprawy, że czasami readLine wykryje, że informacje są dostępne na standardowym wejściu, i od razu spróbuję odczytać linię, zanim B będzie miał szansę zakończyć pisanie linii. Rozwiązałem problem, wprowadzając krótkie opóźnienie między testem końca pliku a odczytem. Wydawało mi się, że opóźnienie wynoszące 5 ms jest dla mnie wystarczające, ale podwoiłem to do 10 ms, aby być po bezpiecznej stronie. To bardzo interesujące, że partia nie cierpi z powodu tego problemu. Omówiliśmy to krótko (5 krótkich postów) na stronie 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>
Dane wyjściowe są takie same jak w przypadku czystego rozwiązania wsadowego, z tym wyjątkiem, że nie ma tam końcowych wierszy „quit”.