不仅可以做到,而且只需批处理文件就可以做到!:-)
通过使用临时文件作为“管道”可以解决该问题。双向通信需要两个“管道”文件。
进程A从“ pipe1”读取stdin并将stdout写入“ pipe2”
进程B从“ pipe2”读取stdin并将stdout写入“ pipe1”
在启动任何一个进程之前,两个文件都必须存在,这一点很重要。文件应在开始时为空。
如果批处理文件尝试从恰好位于当前末尾的文件中读取,它将仅不返回任何内容,并且该文件保持打开状态。因此,我的readLine例程会连续读取,直到获得非空值为止。
我希望能够读取和写入一个空字符串,因此我的writeLine例程附加了一个额外的字符,该字符被readLine剥离了。
我的A流程控制着流程。它通过写入1(消息到B)来初始化事物,然后进入具有10次迭代的循环,在该循环中,它读取一个值(来自B的消息),加1,然后将结果写入(消息至B)。最后,它等待来自B的最后一条消息,然后将“退出”消息写入B并退出。
我的B进程处于有条件的无穷循环中,该循环读取一个值(来自A的消息),将其加10,然后将结果写入(向A的消息)。如果B曾经读过“退出”消息,那么它将立即终止。
我想证明通信是完全同步的,因此在A和B过程循环中都引入了延迟。
请注意,readLine过程处于紧密循环中,在等待输入时会不断滥用CPU和文件系统。可以将PING延迟添加到循环中,但随后的过程将不那么敏感。
我使用真管道来方便地启动A和B进程。但是该管道无法正常工作,因为没有任何通信通过它。所有通讯都是通过我的临时“管道”文件进行的。
我也可以使用START / B来启动进程,但是然后我必须检测它们何时终止,以便我知道何时删除临时“管道”文件。使用管道要简单得多。
我选择将所有代码放在一个文件中,即启动A和B的主脚本以及A和B的代码。我可以为每个进程使用单独的脚本文件。
测试棒
@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。我们只需要一个临时的“管道”文件就可以将信息从B传递回A。因为我们现在有了一个有效的管道,所以A进程不需要向B发送“退出”消息。B进程只是循环直到到达文件末尾。
可以在VBS中使用适当的睡眠功能肯定很好。这使我可以轻松地在readLine函数中引入短暂的延迟,以使CPU休息一下。
但是,阅读中有一种皱纹。最初,我遇到了间歇性故障,直到我意识到有时readLine会检测到stdin上的信息可用,并会在B有机会完成写该行之前立即尝试读取该行。我通过在文件结束测试和读取之间引入短暂的延迟来解决了这个问题。延迟5毫秒似乎对我来说很有效,但为了安全起见,我将延迟增加了一倍至10毫秒。非常有趣的是,该批次没有遇到此问题。我们在http://www.dostips.com/forum/viewtopic.php?f=3&t=7078#p47432上对此进行了简短的讨论(5个简短帖子)。
<!-- : 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>
输出与纯批处理解决方案相同,只是不存在最后的“退出”行。