それを行うことができるだけでなく、バッチファイルだけで行うことができます!:-)
この問題は、一時ファイルを「パイプ」として使用することで解決できます。双方向通信には、2つの「パイプ」ファイルが必要です。
プロセスAは「pipe1」からstdinを読み取り、「pipe2」にstdoutを書き込みます
プロセスBは「pipe2」からstdinを読み取り、「pipe1」にstdoutを書き込みます
どちらかのプロセスを起動する前に、両方のファイルが存在することが重要です。ファイルは最初は空である必要があります。
バッチファイルが現在の末尾にあるファイルから読み取ろうとすると、何も返されず、ファイルは開いたままになります。したがって、私のreadLineルーチンは、空でない値を取得するまで継続的に読み取ります。
空の文字列を読み書きできるようにしたいので、私のwriteLineルーチンは、readLineが取り除く余分な文字を追加します。
マイAプロセスがフローを制御します。1(Bへのメッセージ)を書き込むことで処理を開始し、10回の繰り返しでループに入り、値(Bからのメッセージ)を読み取り、1を加算してから結果(Bへのメッセージ)を書き込みます。最後に、Bからの最後のメッセージを待ってから、「quit」メッセージをBに書き込んで終了します。
私のBプロセスは、値(Aからのメッセージ)を読み取り、10を加算してから結果(Aへのメッセージ)を書き込む条件付き無限ループにあります。Bが「quit」メッセージを読み取ると、すぐに終了します。
通信が完全に同期していることを実証したかったため、AとBの両方のプロセスループに遅延を導入しました。
readLineプロシージャは、入力を待機している間、CPUとファイルシステムの両方を継続的に悪用するタイトループ内にあることに注意してください。ループに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
上級レベルの言語を使用すると、生活が少し楽になります。以下は、AおよびBプロセスにVBScriptを使用する例です。バッチを使用してプロセスを起動します。私は非常にクールな方法はで説明した使用埋め込むことができることと、一時ファイルを使用せずに、バッチファイル内でVBScriptを実行しますか?単一のバッチスクリプト内に複数のVBSスクリプトを埋め込む。
VBSのような上位言語では、通常のパイプを使用してAからBに情報を渡すことができます。BからAに情報を渡すために必要な一時的な「パイプ」ファイルは1つだけです。プロセスは「quit」メッセージをBに送信する必要はありません。Bプロセスは、ファイルの終わりに達するまで単純にループします。
VBSの適切なスリープ機能にアクセスできることは確かに素晴らしいことです。これにより、readLine関数に短い遅延を簡単に導入して、CPUにブレークを与えることができます。
ただし、readLIneには1つのしわがあります。最初は、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>
出力は純粋なバッチソリューションの場合と同じですが、最終的な「終了」行が存在しない点が異なります。