Це не тільки можна зробити, це можна зробити лише за допомогою пакетного файлу! :-)
Проблему можна вирішити, використовуючи тимчасовий файл як "трубу". Двонаправлена комунікація вимагає двох файлів "труби".
Процес A читає stdin з "pipe1" і записує stdout в "pipe2"
Процес B зчитує stdin з "pipe2" і записує stdout в "pipe1"
Важливо, щоб обидва файли існували до запуску будь-якого процесу. Файли на початку повинні бути порожніми.
Якщо пакетний файл намагається прочитати з файлу, який зараз знаходиться в поточному кінці, він просто нічого не повертає, і файл залишається відкритим. Таким чином, моя програма readLine постійно читається, поки не отримає не порожнє значення.
Я хочу вміти читати та писати порожній рядок, тому моя програма writeLine додає додатковий символ, який readLine знімає.
Мій процес контролює потік. Він ініціює речі, записуючи 1 (повідомлення B), а потім вводить цикл з 10 ітераціями, де він читає значення (повідомлення від B), додає 1, а потім записує результат (повідомлення B). Нарешті він чекає останнього повідомлення від B, а потім пише "вийти" з повідомлення в B і виходить.
Мій B процес знаходиться в умовно нескінченному циклі, який зчитує значення (повідомлення від A), додає 10, а потім записує результат (повідомлення A). Якщо коли-небудь читає повідомлення "кинути", воно негайно припиняється.
Я хотів продемонструвати, що зв’язок повністю синхронний, тому я ввожу затримку як в циклі A, так і в B.
Зауважте, що процедура readLine знаходиться в тісному циклі, який постійно зловживає як процесором, так і файловою системою, поки він чекає на введення. Затримка PING може бути додана до циклу, але тоді процеси не будуть настільки чутливими.
Я використовую справжню трубу як зручність для запуску як A, так і B процесів. Але труба нефункціональна тим, що через неї не проходить жодна комунікація. Все спілкування здійснюється через мої тимчасові файли "труби".
Я міг би так само використовувати START / B для запуску процесів, але тоді я повинен виявити, коли вони обидва закінчуються, щоб я знав, коли видалити тимчасові "трубові" файли. Набагато простіше використовувати трубу.
Я вирішив поставити весь код в один файл - головний скрипт, який запускає A і B, а також код для A і B. Я міг би використовувати окремий файл сценарію для кожного процесу.
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
Життя трохи легше з мовою вищого рівня. Нижче наводиться приклад, який використовує VBScript для процесів A і B. Я все ще використовую пакетний запуск процесів. Я використовую дуже класний метод, описаний у " Чи можливо вбудовувати та виконувати VBScript у пакетному файлі без використання тимчасового файлу?" вбудувати декілька сценаріїв VBS в межах одного пакетного сценарію.
З такою вищою мовою, як VBS, ми можемо використовувати звичайну трубу для передачі інформації від A до B. Нам потрібен лише один тимчасовий "трубовий" файл, щоб передати інформацію від B назад до A. Оскільки у нас зараз функціонує труба, A процес не потребує надсилання повідомлення "кинути" до B. Процес B просто циклізується, поки не досягне кінця файлу.
Звичайно, приємно мати доступ до належної функції сну в VBS. Це дозволяє мені легко запровадити коротку затримку функції readLine, щоб перервати процесор.
Однак у readLIne є одна зморшка. Спочатку я отримував переривчасті збої, поки не зрозумів, що іноді readLine виявляє інформацію, доступну на stdin, і негайно спробував би прочитати рядок, перш ніж B мав можливість закінчити писати рядок. Я вирішив проблему, ввівши коротку затримку між тестом кінця файлу та прочитаним. Здається, що затримка в 5 мсек здається для мене хитрістю, але я подвоїв її до 10 мсек, щоб бути на безпечній стороні. Дуже цікаво, що партія не зазнає цього питання. Ми коротко обговорили це питання (5 коротких повідомлень) на 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>
Вихід такий самий, як і для чистого пакетного рішення, за винятком того, що остаточних рядків "кинути" немає.