Это можно сделать не только с помощью командного файла! :-)
Проблема может быть решена путем использования временного файла в качестве "канала". Для двунаправленной связи требуются два "конвейерных" файла.
Процесс A читает стандартный вывод из "pipe1" и записывает стандартный вывод в "pipe2"
Процесс B читает стандартный вывод из "pipe2" и записывает стандартный вывод в "pipe1"
Важно, чтобы оба файла существовали до запуска любого процесса. Файлы должны быть пустыми в начале.
Если пакетный файл пытается прочитать из файла, который находится на текущем конце, он просто ничего не возвращает, и файл остается открытым. Так что моя подпрограмма readLine постоянно читает, пока не получит непустое значение.
Я хочу иметь возможность читать и писать пустую строку, поэтому моя подпрограмма writeLine добавляет дополнительный символ, который readLine удаляет.
Мой процесс A контролирует поток. Он инициирует вещи, записывая 1 (сообщение в B), а затем входит в цикл с 10 итерациями, где читает значение (сообщение из B), добавляет 1 и затем записывает результат (сообщение в B). Наконец, он ожидает последнего сообщения от B, а затем записывает сообщение "выход" в B и завершает работу.
Мой процесс B находится в условно бесконечном цикле, который читает значение (сообщение от A), добавляет 10, а затем записывает результат (сообщение в A). Если B когда-либо прочитает сообщение "выйти", оно немедленно завершится.
Я хотел продемонстрировать, что связь полностью синхронна, поэтому я ввел задержку в обоих циклах процесса A и B.
Обратите внимание, что процедура readLine находится в узком цикле, который непрерывно использует как процессор, так и файловую систему, ожидая ввода. Задержка PING может быть добавлена к циклу, но тогда процессы не будут такими отзывчивыми.
Я использую настоящий канал для удобства запуска процессов A и B. Но канал не работает, так как через него не проходит никакой связи. Все общение происходит через мои временные файлы "pipe".
С тем же успехом я мог бы использовать 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
--ВЫХОД--
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. Нам нужен только один временный файл "pipe" для передачи информации от B обратно к A. Поскольку у нас теперь есть работающий канал, процессу не нужно отправлять сообщение "выход" в B. Процесс B просто зацикливается, пока не достигнет конца файла.
Конечно, приятно иметь доступ к правильной функции сна в VBS. Это позволяет мне легко вводить небольшую задержку в функцию readLine, чтобы дать CPU отключиться.
Однако в 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>
Вывод такой же, как и для чистого пакетного решения, за исключением того, что конечных строк "выход" нет.