Это можно сделать не только с помощью командного файла! :-)
Проблема может быть решена путем использования временного файла в качестве «канала». Для двунаправленной связи требуются два «конвейерных» файла.
Процесс 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. Но канал не работает, так как через него не проходит никакой связи. Все общение происходит через мои временные "трубочные" файлы.
С таким же успехом я мог бы использовать 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. Поскольку у нас теперь есть работающий канал, 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>
Вывод такой же, как и для чистого пакетного решения, за исключением того, что конечных строк "выход" нет.